Fossil SCM

Add infrastructure to support solving the captcha via audio.

drh 2020-03-14 03:13 trunk
Commit 5ddc0fdc6b584f6d50a869be9531d9dd2ef93c6e02a171409b548c79ee73ade4
--- src/captcha.c
+++ src/captcha.c
@@ -608,5 +608,73 @@
608608
captcha_generate(1);
609609
@ </form>
610610
style_footer();
611611
return 1;
612612
}
613
+
614
+/*
615
+** Generate a WAV file that reads aloud the hex digits given by
616
+** zHex.
617
+*/
618
+static void captcha_wav(const char *zHex, Blob *pOut){
619
+ int i;
620
+ const int szWavHdr = 44;
621
+ blob_init(pOut, 0, 0);
622
+ blob_resize(pOut, szWavHdr); /* Space for the WAV header */
623
+ pOut->nUsed = szWavHdr;
624
+ memset(pOut->aData, 0, szWavHdr);
625
+ for(i=0; zHex[i]; i++){
626
+ int v = hex_digit_value(zHex[i]);
627
+ int sz;
628
+ int nData;
629
+ const unsigned char *pData;
630
+ char zSoundName[50];
631
+ sqlite3_snprintf(sizeof(zSoundName),zSoundName,"sounds/%c.wav",
632
+ "0123456789abcdef"[v]);
633
+ pData = builtin_file(zSoundName, &sz);
634
+ nData = sz - szWavHdr;
635
+ blob_resize(pOut, pOut->nUsed+nData);
636
+ memcpy(pOut->aData+pOut->nUsed-nData, pData+szWavHdr, nData);
637
+ if( zHex[i+1]==0 ){
638
+ int len = pOut->nUsed + 36;
639
+ memcpy(pOut->aData, pData, szWavHdr);
640
+ pOut->aData[4] = (char)(len&0xff);
641
+ pOut->aData[5] = (char)((len>>8)&0xff);
642
+ pOut->aData[6] = (char)((len>>16)&0xff);
643
+ pOut->aData[7] = (char)((len>>24)&0xff);
644
+ len = pOut->nUsed;
645
+ pOut->aData[40] = (char)(len&0xff);
646
+ pOut->aData[41] = (char)((len>>8)&0xff);
647
+ pOut->aData[42] = (char)((len>>16)&0xff);
648
+ pOut->aData[43] = (char)((len>>24)&0xff);
649
+ }
650
+ }
651
+}
652
+
653
+/*
654
+** WEBPAGE: /captcha-audio
655
+**
656
+** Return a WAV file that pronounces the digits of the captcha that
657
+** is determined by the seed given in the name= query parameter.
658
+*/
659
+void captcha_wav_page(void){
660
+ const char *zSeed = P("name");
661
+ const char *zDecode = captcha_decode((unsigned int)atoi(zSeed));
662
+ Blob audio;
663
+ captcha_wav(zDecode, &audio);
664
+ cgi_set_content_type("audio/x-wav");
665
+ cgi_set_content(&audio);
666
+}
667
+
668
+/*
669
+** WEBPAGE: /test-captcha-audio
670
+**
671
+** Return a WAV file that pronounces the hex digits of the name=
672
+** query parameter.
673
+*/
674
+void captcha_test_wav_page(void){
675
+ const char *zSeed = P("name");
676
+ Blob audio;
677
+ captcha_wav(zSeed, &audio);
678
+ cgi_set_content_type("audio/x-wav");
679
+ cgi_set_content(&audio);
680
+}
613681
--- src/captcha.c
+++ src/captcha.c
@@ -608,5 +608,73 @@
608 captcha_generate(1);
609 @ </form>
610 style_footer();
611 return 1;
612 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
613
--- src/captcha.c
+++ src/captcha.c
@@ -608,5 +608,73 @@
608 captcha_generate(1);
609 @ </form>
610 style_footer();
611 return 1;
612 }
613
614 /*
615 ** Generate a WAV file that reads aloud the hex digits given by
616 ** zHex.
617 */
618 static void captcha_wav(const char *zHex, Blob *pOut){
619 int i;
620 const int szWavHdr = 44;
621 blob_init(pOut, 0, 0);
622 blob_resize(pOut, szWavHdr); /* Space for the WAV header */
623 pOut->nUsed = szWavHdr;
624 memset(pOut->aData, 0, szWavHdr);
625 for(i=0; zHex[i]; i++){
626 int v = hex_digit_value(zHex[i]);
627 int sz;
628 int nData;
629 const unsigned char *pData;
630 char zSoundName[50];
631 sqlite3_snprintf(sizeof(zSoundName),zSoundName,"sounds/%c.wav",
632 "0123456789abcdef"[v]);
633 pData = builtin_file(zSoundName, &sz);
634 nData = sz - szWavHdr;
635 blob_resize(pOut, pOut->nUsed+nData);
636 memcpy(pOut->aData+pOut->nUsed-nData, pData+szWavHdr, nData);
637 if( zHex[i+1]==0 ){
638 int len = pOut->nUsed + 36;
639 memcpy(pOut->aData, pData, szWavHdr);
640 pOut->aData[4] = (char)(len&0xff);
641 pOut->aData[5] = (char)((len>>8)&0xff);
642 pOut->aData[6] = (char)((len>>16)&0xff);
643 pOut->aData[7] = (char)((len>>24)&0xff);
644 len = pOut->nUsed;
645 pOut->aData[40] = (char)(len&0xff);
646 pOut->aData[41] = (char)((len>>8)&0xff);
647 pOut->aData[42] = (char)((len>>16)&0xff);
648 pOut->aData[43] = (char)((len>>24)&0xff);
649 }
650 }
651 }
652
653 /*
654 ** WEBPAGE: /captcha-audio
655 **
656 ** Return a WAV file that pronounces the digits of the captcha that
657 ** is determined by the seed given in the name= query parameter.
658 */
659 void captcha_wav_page(void){
660 const char *zSeed = P("name");
661 const char *zDecode = captcha_decode((unsigned int)atoi(zSeed));
662 Blob audio;
663 captcha_wav(zDecode, &audio);
664 cgi_set_content_type("audio/x-wav");
665 cgi_set_content(&audio);
666 }
667
668 /*
669 ** WEBPAGE: /test-captcha-audio
670 **
671 ** Return a WAV file that pronounces the hex digits of the name=
672 ** query parameter.
673 */
674 void captcha_test_wav_page(void){
675 const char *zSeed = P("name");
676 Blob audio;
677 captcha_wav(zSeed, &audio);
678 cgi_set_content_type("audio/x-wav");
679 cgi_set_content(&audio);
680 }
681
+17
--- src/main.mk
+++ src/main.mk
@@ -218,16 +218,33 @@
218218
$(SRCDIR)/diff.tcl \
219219
$(SRCDIR)/forum.js \
220220
$(SRCDIR)/graph.js \
221221
$(SRCDIR)/href.js \
222222
$(SRCDIR)/login.js \
223
+ $(SRCDIR)/markdeep.min.js \
223224
$(SRCDIR)/markdown.md \
224225
$(SRCDIR)/menu.js \
225226
$(SRCDIR)/sbsdiff.js \
226227
$(SRCDIR)/scroll.js \
227228
$(SRCDIR)/skin.js \
228229
$(SRCDIR)/sorttable.js \
230
+ $(SRCDIR)/sounds/0.wav \
231
+ $(SRCDIR)/sounds/1.wav \
232
+ $(SRCDIR)/sounds/2.wav \
233
+ $(SRCDIR)/sounds/3.wav \
234
+ $(SRCDIR)/sounds/4.wav \
235
+ $(SRCDIR)/sounds/5.wav \
236
+ $(SRCDIR)/sounds/6.wav \
237
+ $(SRCDIR)/sounds/7.wav \
238
+ $(SRCDIR)/sounds/8.wav \
239
+ $(SRCDIR)/sounds/9.wav \
240
+ $(SRCDIR)/sounds/a.wav \
241
+ $(SRCDIR)/sounds/b.wav \
242
+ $(SRCDIR)/sounds/c.wav \
243
+ $(SRCDIR)/sounds/d.wav \
244
+ $(SRCDIR)/sounds/e.wav \
245
+ $(SRCDIR)/sounds/f.wav \
229246
$(SRCDIR)/tree.js \
230247
$(SRCDIR)/useredit.js \
231248
$(SRCDIR)/wiki.wiki
232249
233250
TRANS_SRC = \
234251
--- src/main.mk
+++ src/main.mk
@@ -218,16 +218,33 @@
218 $(SRCDIR)/diff.tcl \
219 $(SRCDIR)/forum.js \
220 $(SRCDIR)/graph.js \
221 $(SRCDIR)/href.js \
222 $(SRCDIR)/login.js \
 
223 $(SRCDIR)/markdown.md \
224 $(SRCDIR)/menu.js \
225 $(SRCDIR)/sbsdiff.js \
226 $(SRCDIR)/scroll.js \
227 $(SRCDIR)/skin.js \
228 $(SRCDIR)/sorttable.js \
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229 $(SRCDIR)/tree.js \
230 $(SRCDIR)/useredit.js \
231 $(SRCDIR)/wiki.wiki
232
233 TRANS_SRC = \
234
--- src/main.mk
+++ src/main.mk
@@ -218,16 +218,33 @@
218 $(SRCDIR)/diff.tcl \
219 $(SRCDIR)/forum.js \
220 $(SRCDIR)/graph.js \
221 $(SRCDIR)/href.js \
222 $(SRCDIR)/login.js \
223 $(SRCDIR)/markdeep.min.js \
224 $(SRCDIR)/markdown.md \
225 $(SRCDIR)/menu.js \
226 $(SRCDIR)/sbsdiff.js \
227 $(SRCDIR)/scroll.js \
228 $(SRCDIR)/skin.js \
229 $(SRCDIR)/sorttable.js \
230 $(SRCDIR)/sounds/0.wav \
231 $(SRCDIR)/sounds/1.wav \
232 $(SRCDIR)/sounds/2.wav \
233 $(SRCDIR)/sounds/3.wav \
234 $(SRCDIR)/sounds/4.wav \
235 $(SRCDIR)/sounds/5.wav \
236 $(SRCDIR)/sounds/6.wav \
237 $(SRCDIR)/sounds/7.wav \
238 $(SRCDIR)/sounds/8.wav \
239 $(SRCDIR)/sounds/9.wav \
240 $(SRCDIR)/sounds/a.wav \
241 $(SRCDIR)/sounds/b.wav \
242 $(SRCDIR)/sounds/c.wav \
243 $(SRCDIR)/sounds/d.wav \
244 $(SRCDIR)/sounds/e.wav \
245 $(SRCDIR)/sounds/f.wav \
246 $(SRCDIR)/tree.js \
247 $(SRCDIR)/useredit.js \
248 $(SRCDIR)/wiki.wiki
249
250 TRANS_SRC = \
251
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -174,10 +174,11 @@
174174
diff.tcl
175175
markdown.md
176176
wiki.wiki
177177
*.js
178178
../skins/*/*.txt
179
+ sounds/*.wav
179180
}
180181
181182
# Options used to compile the included SQLite library.
182183
#
183184
set SQLITE_OPTIONS {
184185
185186
ADDED src/sounds/0.wav
186187
ADDED src/sounds/1.wav
187188
ADDED src/sounds/2.wav
188189
ADDED src/sounds/3.wav
189190
ADDED src/sounds/4.wav
190191
ADDED src/sounds/5.wav
191192
ADDED src/sounds/6.wav
192193
ADDED src/sounds/7.wav
193194
ADDED src/sounds/8.wav
194195
ADDED src/sounds/9.wav
195196
ADDED src/sounds/README.md
196197
ADDED src/sounds/a.wav
197198
ADDED src/sounds/b.wav
198199
ADDED src/sounds/c.wav
199200
ADDED src/sounds/d.wav
200201
ADDED src/sounds/e.wav
201202
ADDED src/sounds/f.wav
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -174,10 +174,11 @@
174 diff.tcl
175 markdown.md
176 wiki.wiki
177 *.js
178 ../skins/*/*.txt
 
179 }
180
181 # Options used to compile the included SQLite library.
182 #
183 set SQLITE_OPTIONS {
184
185 DDED src/sounds/0.wav
186 DDED src/sounds/1.wav
187 DDED src/sounds/2.wav
188 DDED src/sounds/3.wav
189 DDED src/sounds/4.wav
190 DDED src/sounds/5.wav
191 DDED src/sounds/6.wav
192 DDED src/sounds/7.wav
193 DDED src/sounds/8.wav
194 DDED src/sounds/9.wav
195 DDED src/sounds/README.md
196 DDED src/sounds/a.wav
197 DDED src/sounds/b.wav
198 DDED src/sounds/c.wav
199 DDED src/sounds/d.wav
200 DDED src/sounds/e.wav
201 DDED src/sounds/f.wav
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -174,10 +174,11 @@
174 diff.tcl
175 markdown.md
176 wiki.wiki
177 *.js
178 ../skins/*/*.txt
179 sounds/*.wav
180 }
181
182 # Options used to compile the included SQLite library.
183 #
184 set SQLITE_OPTIONS {
185
186 DDED src/sounds/0.wav
187 DDED src/sounds/1.wav
188 DDED src/sounds/2.wav
189 DDED src/sounds/3.wav
190 DDED src/sounds/4.wav
191 DDED src/sounds/5.wav
192 DDED src/sounds/6.wav
193 DDED src/sounds/7.wav
194 DDED src/sounds/8.wav
195 DDED src/sounds/9.wav
196 DDED src/sounds/README.md
197 DDED src/sounds/a.wav
198 DDED src/sounds/b.wav
199 DDED src/sounds/c.wav
200 DDED src/sounds/d.wav
201 DDED src/sounds/e.wav
202 DDED src/sounds/f.wav

Binary file

Binary file

Binary file

Binary file

Binary file

Binary file

Binary file

Binary file

Binary file

Binary file

--- a/src/sounds/README.md
+++ b/src/sounds/README.md
@@ -0,0 +1,15 @@
1
+The *.wav files in this dire speaking each
2
+of the 16 hexadecimal d consists of just
3
+hexadecimal digits ( generated by the
4
+[../captcha.c module](.. files can be
5
+hen these WAV
6
+files can be con captcha, which
7
+ding of the
8
+captcha, ired users to complete the
9
+captcha.
10
+
11
+Each of the WAV files uses 8000 samples per second, 8 bits per sample
12
+and are 6000 samples in length.
13
+
14
+The recordings are made by Philip Bennefall and are of his own voice.
15
+Mr. Bennefall is ytemself blind and uses this system implemented w
--- a/src/sounds/README.md
+++ b/src/sounds/README.md
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/src/sounds/README.md
+++ b/src/sounds/README.md
@@ -0,0 +1,15 @@
1 The *.wav files in this dire speaking each
2 of the 16 hexadecimal d consists of just
3 hexadecimal digits ( generated by the
4 [../captcha.c module](.. files can be
5 hen these WAV
6 files can be con captcha, which
7 ding of the
8 captcha, ired users to complete the
9 captcha.
10
11 Each of the WAV files uses 8000 samples per second, 8 bits per sample
12 and are 6000 samples in length.
13
14 The recordings are made by Philip Bennefall and are of his own voice.
15 Mr. Bennefall is ytemself blind and uses this system implemented w

Binary file

Binary file

Binary file

Binary file

Binary file

Binary file

--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -640,16 +640,33 @@
640640
$(SRCDIR)/diff.tcl \
641641
$(SRCDIR)/forum.js \
642642
$(SRCDIR)/graph.js \
643643
$(SRCDIR)/href.js \
644644
$(SRCDIR)/login.js \
645
+ $(SRCDIR)/markdeep.min.js \
645646
$(SRCDIR)/markdown.md \
646647
$(SRCDIR)/menu.js \
647648
$(SRCDIR)/sbsdiff.js \
648649
$(SRCDIR)/scroll.js \
649650
$(SRCDIR)/skin.js \
650651
$(SRCDIR)/sorttable.js \
652
+ $(SRCDIR)/sounds/0.wav \
653
+ $(SRCDIR)/sounds/1.wav \
654
+ $(SRCDIR)/sounds/2.wav \
655
+ $(SRCDIR)/sounds/3.wav \
656
+ $(SRCDIR)/sounds/4.wav \
657
+ $(SRCDIR)/sounds/5.wav \
658
+ $(SRCDIR)/sounds/6.wav \
659
+ $(SRCDIR)/sounds/7.wav \
660
+ $(SRCDIR)/sounds/8.wav \
661
+ $(SRCDIR)/sounds/9.wav \
662
+ $(SRCDIR)/sounds/a.wav \
663
+ $(SRCDIR)/sounds/b.wav \
664
+ $(SRCDIR)/sounds/c.wav \
665
+ $(SRCDIR)/sounds/d.wav \
666
+ $(SRCDIR)/sounds/e.wav \
667
+ $(SRCDIR)/sounds/f.wav \
651668
$(SRCDIR)/tree.js \
652669
$(SRCDIR)/useredit.js \
653670
$(SRCDIR)/wiki.wiki
654671
655672
TRANS_SRC = \
656673
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -640,16 +640,33 @@
640 $(SRCDIR)/diff.tcl \
641 $(SRCDIR)/forum.js \
642 $(SRCDIR)/graph.js \
643 $(SRCDIR)/href.js \
644 $(SRCDIR)/login.js \
 
645 $(SRCDIR)/markdown.md \
646 $(SRCDIR)/menu.js \
647 $(SRCDIR)/sbsdiff.js \
648 $(SRCDIR)/scroll.js \
649 $(SRCDIR)/skin.js \
650 $(SRCDIR)/sorttable.js \
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
651 $(SRCDIR)/tree.js \
652 $(SRCDIR)/useredit.js \
653 $(SRCDIR)/wiki.wiki
654
655 TRANS_SRC = \
656
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -640,16 +640,33 @@
640 $(SRCDIR)/diff.tcl \
641 $(SRCDIR)/forum.js \
642 $(SRCDIR)/graph.js \
643 $(SRCDIR)/href.js \
644 $(SRCDIR)/login.js \
645 $(SRCDIR)/markdeep.min.js \
646 $(SRCDIR)/markdown.md \
647 $(SRCDIR)/menu.js \
648 $(SRCDIR)/sbsdiff.js \
649 $(SRCDIR)/scroll.js \
650 $(SRCDIR)/skin.js \
651 $(SRCDIR)/sorttable.js \
652 $(SRCDIR)/sounds/0.wav \
653 $(SRCDIR)/sounds/1.wav \
654 $(SRCDIR)/sounds/2.wav \
655 $(SRCDIR)/sounds/3.wav \
656 $(SRCDIR)/sounds/4.wav \
657 $(SRCDIR)/sounds/5.wav \
658 $(SRCDIR)/sounds/6.wav \
659 $(SRCDIR)/sounds/7.wav \
660 $(SRCDIR)/sounds/8.wav \
661 $(SRCDIR)/sounds/9.wav \
662 $(SRCDIR)/sounds/a.wav \
663 $(SRCDIR)/sounds/b.wav \
664 $(SRCDIR)/sounds/c.wav \
665 $(SRCDIR)/sounds/d.wav \
666 $(SRCDIR)/sounds/e.wav \
667 $(SRCDIR)/sounds/f.wav \
668 $(SRCDIR)/tree.js \
669 $(SRCDIR)/useredit.js \
670 $(SRCDIR)/wiki.wiki
671
672 TRANS_SRC = \
673
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -547,16 +547,33 @@
547547
$(SRCDIR)\diff.tcl \
548548
$(SRCDIR)\forum.js \
549549
$(SRCDIR)\graph.js \
550550
$(SRCDIR)\href.js \
551551
$(SRCDIR)\login.js \
552
+ $(SRCDIR)\markdeep.min.js \
552553
$(SRCDIR)\markdown.md \
553554
$(SRCDIR)\menu.js \
554555
$(SRCDIR)\sbsdiff.js \
555556
$(SRCDIR)\scroll.js \
556557
$(SRCDIR)\skin.js \
557558
$(SRCDIR)\sorttable.js \
559
+ $(SRCDIR)\sounds\0.wav \
560
+ $(SRCDIR)\sounds\1.wav \
561
+ $(SRCDIR)\sounds\2.wav \
562
+ $(SRCDIR)\sounds\3.wav \
563
+ $(SRCDIR)\sounds\4.wav \
564
+ $(SRCDIR)\sounds\5.wav \
565
+ $(SRCDIR)\sounds\6.wav \
566
+ $(SRCDIR)\sounds\7.wav \
567
+ $(SRCDIR)\sounds\8.wav \
568
+ $(SRCDIR)\sounds\9.wav \
569
+ $(SRCDIR)\sounds\a.wav \
570
+ $(SRCDIR)\sounds\b.wav \
571
+ $(SRCDIR)\sounds\c.wav \
572
+ $(SRCDIR)\sounds\d.wav \
573
+ $(SRCDIR)\sounds\e.wav \
574
+ $(SRCDIR)\sounds\f.wav \
558575
$(SRCDIR)\tree.js \
559576
$(SRCDIR)\useredit.js \
560577
$(SRCDIR)\wiki.wiki
561578
562579
OBJ = $(OX)\add$O \
563580
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -547,16 +547,33 @@
547 $(SRCDIR)\diff.tcl \
548 $(SRCDIR)\forum.js \
549 $(SRCDIR)\graph.js \
550 $(SRCDIR)\href.js \
551 $(SRCDIR)\login.js \
 
552 $(SRCDIR)\markdown.md \
553 $(SRCDIR)\menu.js \
554 $(SRCDIR)\sbsdiff.js \
555 $(SRCDIR)\scroll.js \
556 $(SRCDIR)\skin.js \
557 $(SRCDIR)\sorttable.js \
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
558 $(SRCDIR)\tree.js \
559 $(SRCDIR)\useredit.js \
560 $(SRCDIR)\wiki.wiki
561
562 OBJ = $(OX)\add$O \
563
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -547,16 +547,33 @@
547 $(SRCDIR)\diff.tcl \
548 $(SRCDIR)\forum.js \
549 $(SRCDIR)\graph.js \
550 $(SRCDIR)\href.js \
551 $(SRCDIR)\login.js \
552 $(SRCDIR)\markdeep.min.js \
553 $(SRCDIR)\markdown.md \
554 $(SRCDIR)\menu.js \
555 $(SRCDIR)\sbsdiff.js \
556 $(SRCDIR)\scroll.js \
557 $(SRCDIR)\skin.js \
558 $(SRCDIR)\sorttable.js \
559 $(SRCDIR)\sounds\0.wav \
560 $(SRCDIR)\sounds\1.wav \
561 $(SRCDIR)\sounds\2.wav \
562 $(SRCDIR)\sounds\3.wav \
563 $(SRCDIR)\sounds\4.wav \
564 $(SRCDIR)\sounds\5.wav \
565 $(SRCDIR)\sounds\6.wav \
566 $(SRCDIR)\sounds\7.wav \
567 $(SRCDIR)\sounds\8.wav \
568 $(SRCDIR)\sounds\9.wav \
569 $(SRCDIR)\sounds\a.wav \
570 $(SRCDIR)\sounds\b.wav \
571 $(SRCDIR)\sounds\c.wav \
572 $(SRCDIR)\sounds\d.wav \
573 $(SRCDIR)\sounds\e.wav \
574 $(SRCDIR)\sounds\f.wav \
575 $(SRCDIR)\tree.js \
576 $(SRCDIR)\useredit.js \
577 $(SRCDIR)\wiki.wiki
578
579 OBJ = $(OX)\add$O \
580

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button