Fossil SCM

Merge the self-registration changes into the trunk.

drh 2011-01-04 18:05 trunk merge
Commit 9039a6abed2e8d73cd36ac0ff06ce736bfc18d45
+11
--- src/db.c
+++ src/db.c
@@ -1584,10 +1584,11 @@
15841584
{ "auto-captcha", "autocaptcha", 0, "on" },
15851585
{ "auto-shun", 0, 0, "on" },
15861586
{ "autosync", 0, 0, "on" },
15871587
{ "binary-glob", 0, 32, "" },
15881588
{ "clearsign", 0, 0, "off" },
1589
+ { "default-perms", 0, 16, "u" },
15891590
{ "diff-command", 0, 16, "" },
15901591
{ "dont-push", 0, 0, "off" },
15911592
{ "editor", 0, 16, "" },
15921593
{ "gdiff-command", 0, 16, "gdiff" },
15931594
{ "ignore-glob", 0, 40, "" },
@@ -1596,10 +1597,11 @@
15961597
{ "manifest", 0, 0, "off" },
15971598
{ "mtime-changes", 0, 0, "on" },
15981599
{ "pgp-command", 0, 32, "gpg --clearsign -o " },
15991600
{ "proxy", 0, 32, "off" },
16001601
{ "repo-cksum", 0, 0, "on" },
1602
+ { "self-register", 0, 0, "off" },
16011603
{ "ssh-command", 0, 32, "" },
16021604
{ "web-browser", 0, 32, "" },
16031605
{ 0,0,0,0 }
16041606
};
16051607
@@ -1635,10 +1637,14 @@
16351637
** purposes. Example: *.xml
16361638
**
16371639
** clearsign When enabled, fossil will attempt to sign all commits
16381640
** with gpg. When disabled (the default), commits will
16391641
** be unsigned. Default: off
1642
+**
1643
+** default-perms Permissions given automatically to new users. For more
1644
+** information on permissions see Users page in Server
1645
+** Administration of the HTTP UI. Default: u.
16401646
**
16411647
** diff-command External command to run when performing a diff.
16421648
** If undefined, the internal text diff will be used.
16431649
**
16441650
** dont-push Prevent this repository from pushing from client to
@@ -1678,10 +1684,15 @@
16781684
**
16791685
** repo-cksum Compute checksums over all files in each checkout
16801686
** as a double-check of correctness. Defaults to "on".
16811687
** Disable on large repositories for a performance
16821688
** improvement.
1689
+**
1690
+** self-register Allow users to register themselves through the HTTP UI.
1691
+** This is useful if you want to see other names than
1692
+** "Anonymous" in e.g. ticketing system. On the other hand
1693
+** users can not be deleted. Default: off.
16831694
**
16841695
** ssh-command Command used to talk to a remote machine with
16851696
** the "ssh://" protocol.
16861697
**
16871698
** web-browser A shell command used to launch your preferred
16881699
--- src/db.c
+++ src/db.c
@@ -1584,10 +1584,11 @@
1584 { "auto-captcha", "autocaptcha", 0, "on" },
1585 { "auto-shun", 0, 0, "on" },
1586 { "autosync", 0, 0, "on" },
1587 { "binary-glob", 0, 32, "" },
1588 { "clearsign", 0, 0, "off" },
 
1589 { "diff-command", 0, 16, "" },
1590 { "dont-push", 0, 0, "off" },
1591 { "editor", 0, 16, "" },
1592 { "gdiff-command", 0, 16, "gdiff" },
1593 { "ignore-glob", 0, 40, "" },
@@ -1596,10 +1597,11 @@
1596 { "manifest", 0, 0, "off" },
1597 { "mtime-changes", 0, 0, "on" },
1598 { "pgp-command", 0, 32, "gpg --clearsign -o " },
1599 { "proxy", 0, 32, "off" },
1600 { "repo-cksum", 0, 0, "on" },
 
1601 { "ssh-command", 0, 32, "" },
1602 { "web-browser", 0, 32, "" },
1603 { 0,0,0,0 }
1604 };
1605
@@ -1635,10 +1637,14 @@
1635 ** purposes. Example: *.xml
1636 **
1637 ** clearsign When enabled, fossil will attempt to sign all commits
1638 ** with gpg. When disabled (the default), commits will
1639 ** be unsigned. Default: off
 
 
 
 
1640 **
1641 ** diff-command External command to run when performing a diff.
1642 ** If undefined, the internal text diff will be used.
1643 **
1644 ** dont-push Prevent this repository from pushing from client to
@@ -1678,10 +1684,15 @@
1678 **
1679 ** repo-cksum Compute checksums over all files in each checkout
1680 ** as a double-check of correctness. Defaults to "on".
1681 ** Disable on large repositories for a performance
1682 ** improvement.
 
 
 
 
 
1683 **
1684 ** ssh-command Command used to talk to a remote machine with
1685 ** the "ssh://" protocol.
1686 **
1687 ** web-browser A shell command used to launch your preferred
1688
--- src/db.c
+++ src/db.c
@@ -1584,10 +1584,11 @@
1584 { "auto-captcha", "autocaptcha", 0, "on" },
1585 { "auto-shun", 0, 0, "on" },
1586 { "autosync", 0, 0, "on" },
1587 { "binary-glob", 0, 32, "" },
1588 { "clearsign", 0, 0, "off" },
1589 { "default-perms", 0, 16, "u" },
1590 { "diff-command", 0, 16, "" },
1591 { "dont-push", 0, 0, "off" },
1592 { "editor", 0, 16, "" },
1593 { "gdiff-command", 0, 16, "gdiff" },
1594 { "ignore-glob", 0, 40, "" },
@@ -1596,10 +1597,11 @@
1597 { "manifest", 0, 0, "off" },
1598 { "mtime-changes", 0, 0, "on" },
1599 { "pgp-command", 0, 32, "gpg --clearsign -o " },
1600 { "proxy", 0, 32, "off" },
1601 { "repo-cksum", 0, 0, "on" },
1602 { "self-register", 0, 0, "off" },
1603 { "ssh-command", 0, 32, "" },
1604 { "web-browser", 0, 32, "" },
1605 { 0,0,0,0 }
1606 };
1607
@@ -1635,10 +1637,14 @@
1637 ** purposes. Example: *.xml
1638 **
1639 ** clearsign When enabled, fossil will attempt to sign all commits
1640 ** with gpg. When disabled (the default), commits will
1641 ** be unsigned. Default: off
1642 **
1643 ** default-perms Permissions given automatically to new users. For more
1644 ** information on permissions see Users page in Server
1645 ** Administration of the HTTP UI. Default: u.
1646 **
1647 ** diff-command External command to run when performing a diff.
1648 ** If undefined, the internal text diff will be used.
1649 **
1650 ** dont-push Prevent this repository from pushing from client to
@@ -1678,10 +1684,15 @@
1684 **
1685 ** repo-cksum Compute checksums over all files in each checkout
1686 ** as a double-check of correctness. Defaults to "on".
1687 ** Disable on large repositories for a performance
1688 ** improvement.
1689 **
1690 ** self-register Allow users to register themselves through the HTTP UI.
1691 ** This is useful if you want to see other names than
1692 ** "Anonymous" in e.g. ticketing system. On the other hand
1693 ** users can not be deleted. Default: off.
1694 **
1695 ** ssh-command Command used to talk to a remote machine with
1696 ** the "ssh://" protocol.
1697 **
1698 ** web-browser A shell command used to launch your preferred
1699
+11
--- src/db.c
+++ src/db.c
@@ -1584,10 +1584,11 @@
15841584
{ "auto-captcha", "autocaptcha", 0, "on" },
15851585
{ "auto-shun", 0, 0, "on" },
15861586
{ "autosync", 0, 0, "on" },
15871587
{ "binary-glob", 0, 32, "" },
15881588
{ "clearsign", 0, 0, "off" },
1589
+ { "default-perms", 0, 16, "u" },
15891590
{ "diff-command", 0, 16, "" },
15901591
{ "dont-push", 0, 0, "off" },
15911592
{ "editor", 0, 16, "" },
15921593
{ "gdiff-command", 0, 16, "gdiff" },
15931594
{ "ignore-glob", 0, 40, "" },
@@ -1596,10 +1597,11 @@
15961597
{ "manifest", 0, 0, "off" },
15971598
{ "mtime-changes", 0, 0, "on" },
15981599
{ "pgp-command", 0, 32, "gpg --clearsign -o " },
15991600
{ "proxy", 0, 32, "off" },
16001601
{ "repo-cksum", 0, 0, "on" },
1602
+ { "self-register", 0, 0, "off" },
16011603
{ "ssh-command", 0, 32, "" },
16021604
{ "web-browser", 0, 32, "" },
16031605
{ 0,0,0,0 }
16041606
};
16051607
@@ -1635,10 +1637,14 @@
16351637
** purposes. Example: *.xml
16361638
**
16371639
** clearsign When enabled, fossil will attempt to sign all commits
16381640
** with gpg. When disabled (the default), commits will
16391641
** be unsigned. Default: off
1642
+**
1643
+** default-perms Permissions given automatically to new users. For more
1644
+** information on permissions see Users page in Server
1645
+** Administration of the HTTP UI. Default: u.
16401646
**
16411647
** diff-command External command to run when performing a diff.
16421648
** If undefined, the internal text diff will be used.
16431649
**
16441650
** dont-push Prevent this repository from pushing from client to
@@ -1678,10 +1684,15 @@
16781684
**
16791685
** repo-cksum Compute checksums over all files in each checkout
16801686
** as a double-check of correctness. Defaults to "on".
16811687
** Disable on large repositories for a performance
16821688
** improvement.
1689
+**
1690
+** self-register Allow users to register themselves through the HTTP UI.
1691
+** This is useful if you want to see other names than
1692
+** "Anonymous" in e.g. ticketing system. On the other hand
1693
+** users can not be deleted. Default: off.
16831694
**
16841695
** ssh-command Command used to talk to a remote machine with
16851696
** the "ssh://" protocol.
16861697
**
16871698
** web-browser A shell command used to launch your preferred
16881699
--- src/db.c
+++ src/db.c
@@ -1584,10 +1584,11 @@
1584 { "auto-captcha", "autocaptcha", 0, "on" },
1585 { "auto-shun", 0, 0, "on" },
1586 { "autosync", 0, 0, "on" },
1587 { "binary-glob", 0, 32, "" },
1588 { "clearsign", 0, 0, "off" },
 
1589 { "diff-command", 0, 16, "" },
1590 { "dont-push", 0, 0, "off" },
1591 { "editor", 0, 16, "" },
1592 { "gdiff-command", 0, 16, "gdiff" },
1593 { "ignore-glob", 0, 40, "" },
@@ -1596,10 +1597,11 @@
1596 { "manifest", 0, 0, "off" },
1597 { "mtime-changes", 0, 0, "on" },
1598 { "pgp-command", 0, 32, "gpg --clearsign -o " },
1599 { "proxy", 0, 32, "off" },
1600 { "repo-cksum", 0, 0, "on" },
 
1601 { "ssh-command", 0, 32, "" },
1602 { "web-browser", 0, 32, "" },
1603 { 0,0,0,0 }
1604 };
1605
@@ -1635,10 +1637,14 @@
1635 ** purposes. Example: *.xml
1636 **
1637 ** clearsign When enabled, fossil will attempt to sign all commits
1638 ** with gpg. When disabled (the default), commits will
1639 ** be unsigned. Default: off
 
 
 
 
1640 **
1641 ** diff-command External command to run when performing a diff.
1642 ** If undefined, the internal text diff will be used.
1643 **
1644 ** dont-push Prevent this repository from pushing from client to
@@ -1678,10 +1684,15 @@
1678 **
1679 ** repo-cksum Compute checksums over all files in each checkout
1680 ** as a double-check of correctness. Defaults to "on".
1681 ** Disable on large repositories for a performance
1682 ** improvement.
 
 
 
 
 
1683 **
1684 ** ssh-command Command used to talk to a remote machine with
1685 ** the "ssh://" protocol.
1686 **
1687 ** web-browser A shell command used to launch your preferred
1688
--- src/db.c
+++ src/db.c
@@ -1584,10 +1584,11 @@
1584 { "auto-captcha", "autocaptcha", 0, "on" },
1585 { "auto-shun", 0, 0, "on" },
1586 { "autosync", 0, 0, "on" },
1587 { "binary-glob", 0, 32, "" },
1588 { "clearsign", 0, 0, "off" },
1589 { "default-perms", 0, 16, "u" },
1590 { "diff-command", 0, 16, "" },
1591 { "dont-push", 0, 0, "off" },
1592 { "editor", 0, 16, "" },
1593 { "gdiff-command", 0, 16, "gdiff" },
1594 { "ignore-glob", 0, 40, "" },
@@ -1596,10 +1597,11 @@
1597 { "manifest", 0, 0, "off" },
1598 { "mtime-changes", 0, 0, "on" },
1599 { "pgp-command", 0, 32, "gpg --clearsign -o " },
1600 { "proxy", 0, 32, "off" },
1601 { "repo-cksum", 0, 0, "on" },
1602 { "self-register", 0, 0, "off" },
1603 { "ssh-command", 0, 32, "" },
1604 { "web-browser", 0, 32, "" },
1605 { 0,0,0,0 }
1606 };
1607
@@ -1635,10 +1637,14 @@
1637 ** purposes. Example: *.xml
1638 **
1639 ** clearsign When enabled, fossil will attempt to sign all commits
1640 ** with gpg. When disabled (the default), commits will
1641 ** be unsigned. Default: off
1642 **
1643 ** default-perms Permissions given automatically to new users. For more
1644 ** information on permissions see Users page in Server
1645 ** Administration of the HTTP UI. Default: u.
1646 **
1647 ** diff-command External command to run when performing a diff.
1648 ** If undefined, the internal text diff will be used.
1649 **
1650 ** dont-push Prevent this repository from pushing from client to
@@ -1678,10 +1684,15 @@
1684 **
1685 ** repo-cksum Compute checksums over all files in each checkout
1686 ** as a double-check of correctness. Defaults to "on".
1687 ** Disable on large repositories for a performance
1688 ** improvement.
1689 **
1690 ** self-register Allow users to register themselves through the HTTP UI.
1691 ** This is useful if you want to see other names than
1692 ** "Anonymous" in e.g. ticketing system. On the other hand
1693 ** users can not be deleted. Default: off.
1694 **
1695 ** ssh-command Command used to talk to a remote machine with
1696 ** the "ssh://" protocol.
1697 **
1698 ** web-browser A shell command used to launch your preferred
1699
+137
--- src/login.c
+++ src/login.c
@@ -266,10 +266,14 @@
266266
}
267267
@ your user-id and password at the left and press the
268268
@ "Login" button. Your user name will be stored in a browser cookie.
269269
@ You must configure your web browser to accept cookies in order for
270270
@ the login to take.</p>
271
+ if( db_get_boolean("self-register", 0) ){
272
+ @ <p>If you do not have an account, you can
273
+ @ <a href="%s(g.zTop)/register?g=%T(P("G"))">create one</a>.
274
+ }
271275
if( zAnonPw ){
272276
unsigned int uSeed = captcha_seed();
273277
char const *zDecoded = captcha_decode(uSeed);
274278
int bAutoCaptcha = db_get_boolean("auto-captcha", 1);
275279
char *zCaptcha = captcha_render(zDecoded);
@@ -623,5 +627,138 @@
623627
g.okCsrf = 1;
624628
return;
625629
}
626630
fossil_fatal("Cross-site request forgery attempt");
627631
}
632
+
633
+/*
634
+** WEBPAGE: register
635
+**
636
+** Generate the register page.
637
+**
638
+*/
639
+void register_page(void){
640
+ const char *zUsername, *zPasswd, *zConfirm, *zContact, *zCS, *zPw, *zCap;
641
+ if( !db_get_boolean("self-register", 0) ){
642
+ style_header("Registration not possible");
643
+ @ <p>This project does not allow user self-registration. Please contact the
644
+ @ project administrator to obtain an account.</p>
645
+ style_footer();
646
+ return;
647
+ }
648
+
649
+ style_header("Register");
650
+ zUsername = P("u");
651
+ zPasswd = P("p");
652
+ zConfirm = P("cp");
653
+ zContact = P("c");
654
+ zCap = P("cap");
655
+ zCS = P("cs"); /* Captcha Secret */
656
+
657
+ /* Try to make any sense from user input. */
658
+ if( P("new") ){
659
+ if( zCS==0 ) fossil_redirect_home(); /* Forged request */
660
+ zPw = captcha_decode((unsigned int)atoi(zCS));
661
+ if( !(zUsername && zPasswd && zConfirm && zContact) ){
662
+ @ <p><span class="loginError">
663
+ @ All fields are obligatory.
664
+ @ </span></p>
665
+ }else if( strlen(zPasswd) < 6){
666
+ @ <p><span class="loginError">
667
+ @ Password too weak.
668
+ @ </span></p>
669
+ }else if( strcmp(zPasswd,zConfirm)!=0 ){
670
+ @ <p><span class="loginError">
671
+ @ The two copies of your new passwords do not match.
672
+ @ </span></p>
673
+ }else if( strcasecmp(zPw, zCap)!=0 ){
674
+ @ <p><span class="loginError">
675
+ @ Captcha text invalid.
676
+ @ </span></p>
677
+ }else{
678
+ /* This almost is stupid copy-paste of code from user.c:user_cmd(). */
679
+ Blob passwd, login, caps, contact;
680
+
681
+ blob_init(&login, zUsername, -1);
682
+ blob_init(&contact, zContact, -1);
683
+ blob_init(&caps, db_get("default-perms", "u"), -1);
684
+ blob_init(&passwd, zPasswd, -1);
685
+
686
+ if( db_exists("SELECT 1 FROM user WHERE login=%B", &login) ){
687
+ /* Here lies the reason I don't use zErrMsg - it would not substitute
688
+ * this %s(zUsername), or at least I don't know how to force it to.*/
689
+ @ <p><span class="loginError">
690
+ @ %s(zUsername) already exists.
691
+ @ </span></p>
692
+ }else{
693
+ char *zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login));
694
+ db_multi_exec(
695
+ "INSERT INTO user(login,pw,cap,info)"
696
+ "VALUES(%B,%Q,%B,%B)",
697
+ &login, zPw, &caps, &contact
698
+ );
699
+ free(zPw);
700
+
701
+ /* The user is registered, now just log him in. */
702
+ int uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUsername);
703
+ char *zCookie;
704
+ const char *zCookieName = login_cookie_name();
705
+ const char *zExpire = db_get("cookie-expire","8766");
706
+ int expires = atoi(zExpire)*3600;
707
+ const char *zIpAddr = PD("REMOTE_ADDR","nil");
708
+
709
+ zCookie = db_text(0, "SELECT '%d/' || hex(randomblob(25))", uid);
710
+ cgi_set_cookie(zCookieName, zCookie, 0, expires);
711
+ db_multi_exec(
712
+ "UPDATE user SET cookie=%Q, ipaddr=%Q, "
713
+ " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d",
714
+ zCookie, zIpAddr, expires, uid
715
+ );
716
+ redirect_to_g();
717
+
718
+ }
719
+ }
720
+ }
721
+
722
+ /* Prepare the captcha. */
723
+ unsigned int uSeed = captcha_seed();
724
+ char const *zDecoded = captcha_decode(uSeed);
725
+ char *zCaptcha = captcha_render(zDecoded);
726
+
727
+ /* Print out the registration form. */
728
+ @ <form action="register" method="post">
729
+ if( P("g") ){
730
+ @ <input type="hidden" name="g" value="%h(P("g"))" />
731
+ }
732
+ @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
733
+ @ <table class="login_out">
734
+ @ <tr>
735
+ @ <td class="login_out_label" align="right">User ID:</td>
736
+ @ <td><input type="text" id="u" name="u" value="" size="30" /></td>
737
+ @ </tr>
738
+ @ <tr>
739
+ @ <td class="login_out_label" align="right">Password:</td>
740
+ @ <td><input type="password" id="p" name="p" value="" size="30" /></td>
741
+ @ </tr>
742
+ @ <tr>
743
+ @ <td class="login_out_label" align="right">Confirm password:</td>
744
+ @ <td><input type="password" id="cp" name="cp" value="" size="30" /></td>
745
+ @ </tr>
746
+ @ <tr>
747
+ @ <td class="login_out_label" align="right">Contact info:</td>
748
+ @ <td><input type="text" id="c" name="c" value="" size="30" /></td>
749
+ @ </tr>
750
+ @ <tr>
751
+ @ <td class="login_out_label" align="right">Captcha text (below):</td>
752
+ @ <td><input type="text" id="cap" name="cap" value="" size="30" /></td>
753
+ @ </tr>
754
+ @ <tr><td></td>
755
+ @ <td><input type="submit" name="new" value="Register" /></td></tr>
756
+ @ </table>
757
+ @ <div class="captcha"><table class="captcha"><tr><td><pre>
758
+ @ %s(zCaptcha)
759
+ @ </pre></td></tr></table>
760
+ @ </form>
761
+ style_footer();
762
+
763
+ free(zCaptcha);
764
+}
628765
--- src/login.c
+++ src/login.c
@@ -266,10 +266,14 @@
266 }
267 @ your user-id and password at the left and press the
268 @ "Login" button. Your user name will be stored in a browser cookie.
269 @ You must configure your web browser to accept cookies in order for
270 @ the login to take.</p>
 
 
 
 
271 if( zAnonPw ){
272 unsigned int uSeed = captcha_seed();
273 char const *zDecoded = captcha_decode(uSeed);
274 int bAutoCaptcha = db_get_boolean("auto-captcha", 1);
275 char *zCaptcha = captcha_render(zDecoded);
@@ -623,5 +627,138 @@
623 g.okCsrf = 1;
624 return;
625 }
626 fossil_fatal("Cross-site request forgery attempt");
627 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
628
--- src/login.c
+++ src/login.c
@@ -266,10 +266,14 @@
266 }
267 @ your user-id and password at the left and press the
268 @ "Login" button. Your user name will be stored in a browser cookie.
269 @ You must configure your web browser to accept cookies in order for
270 @ the login to take.</p>
271 if( db_get_boolean("self-register", 0) ){
272 @ <p>If you do not have an account, you can
273 @ <a href="%s(g.zTop)/register?g=%T(P("G"))">create one</a>.
274 }
275 if( zAnonPw ){
276 unsigned int uSeed = captcha_seed();
277 char const *zDecoded = captcha_decode(uSeed);
278 int bAutoCaptcha = db_get_boolean("auto-captcha", 1);
279 char *zCaptcha = captcha_render(zDecoded);
@@ -623,5 +627,138 @@
627 g.okCsrf = 1;
628 return;
629 }
630 fossil_fatal("Cross-site request forgery attempt");
631 }
632
633 /*
634 ** WEBPAGE: register
635 **
636 ** Generate the register page.
637 **
638 */
639 void register_page(void){
640 const char *zUsername, *zPasswd, *zConfirm, *zContact, *zCS, *zPw, *zCap;
641 if( !db_get_boolean("self-register", 0) ){
642 style_header("Registration not possible");
643 @ <p>This project does not allow user self-registration. Please contact the
644 @ project administrator to obtain an account.</p>
645 style_footer();
646 return;
647 }
648
649 style_header("Register");
650 zUsername = P("u");
651 zPasswd = P("p");
652 zConfirm = P("cp");
653 zContact = P("c");
654 zCap = P("cap");
655 zCS = P("cs"); /* Captcha Secret */
656
657 /* Try to make any sense from user input. */
658 if( P("new") ){
659 if( zCS==0 ) fossil_redirect_home(); /* Forged request */
660 zPw = captcha_decode((unsigned int)atoi(zCS));
661 if( !(zUsername && zPasswd && zConfirm && zContact) ){
662 @ <p><span class="loginError">
663 @ All fields are obligatory.
664 @ </span></p>
665 }else if( strlen(zPasswd) < 6){
666 @ <p><span class="loginError">
667 @ Password too weak.
668 @ </span></p>
669 }else if( strcmp(zPasswd,zConfirm)!=0 ){
670 @ <p><span class="loginError">
671 @ The two copies of your new passwords do not match.
672 @ </span></p>
673 }else if( strcasecmp(zPw, zCap)!=0 ){
674 @ <p><span class="loginError">
675 @ Captcha text invalid.
676 @ </span></p>
677 }else{
678 /* This almost is stupid copy-paste of code from user.c:user_cmd(). */
679 Blob passwd, login, caps, contact;
680
681 blob_init(&login, zUsername, -1);
682 blob_init(&contact, zContact, -1);
683 blob_init(&caps, db_get("default-perms", "u"), -1);
684 blob_init(&passwd, zPasswd, -1);
685
686 if( db_exists("SELECT 1 FROM user WHERE login=%B", &login) ){
687 /* Here lies the reason I don't use zErrMsg - it would not substitute
688 * this %s(zUsername), or at least I don't know how to force it to.*/
689 @ <p><span class="loginError">
690 @ %s(zUsername) already exists.
691 @ </span></p>
692 }else{
693 char *zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login));
694 db_multi_exec(
695 "INSERT INTO user(login,pw,cap,info)"
696 "VALUES(%B,%Q,%B,%B)",
697 &login, zPw, &caps, &contact
698 );
699 free(zPw);
700
701 /* The user is registered, now just log him in. */
702 int uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUsername);
703 char *zCookie;
704 const char *zCookieName = login_cookie_name();
705 const char *zExpire = db_get("cookie-expire","8766");
706 int expires = atoi(zExpire)*3600;
707 const char *zIpAddr = PD("REMOTE_ADDR","nil");
708
709 zCookie = db_text(0, "SELECT '%d/' || hex(randomblob(25))", uid);
710 cgi_set_cookie(zCookieName, zCookie, 0, expires);
711 db_multi_exec(
712 "UPDATE user SET cookie=%Q, ipaddr=%Q, "
713 " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d",
714 zCookie, zIpAddr, expires, uid
715 );
716 redirect_to_g();
717
718 }
719 }
720 }
721
722 /* Prepare the captcha. */
723 unsigned int uSeed = captcha_seed();
724 char const *zDecoded = captcha_decode(uSeed);
725 char *zCaptcha = captcha_render(zDecoded);
726
727 /* Print out the registration form. */
728 @ <form action="register" method="post">
729 if( P("g") ){
730 @ <input type="hidden" name="g" value="%h(P("g"))" />
731 }
732 @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
733 @ <table class="login_out">
734 @ <tr>
735 @ <td class="login_out_label" align="right">User ID:</td>
736 @ <td><input type="text" id="u" name="u" value="" size="30" /></td>
737 @ </tr>
738 @ <tr>
739 @ <td class="login_out_label" align="right">Password:</td>
740 @ <td><input type="password" id="p" name="p" value="" size="30" /></td>
741 @ </tr>
742 @ <tr>
743 @ <td class="login_out_label" align="right">Confirm password:</td>
744 @ <td><input type="password" id="cp" name="cp" value="" size="30" /></td>
745 @ </tr>
746 @ <tr>
747 @ <td class="login_out_label" align="right">Contact info:</td>
748 @ <td><input type="text" id="c" name="c" value="" size="30" /></td>
749 @ </tr>
750 @ <tr>
751 @ <td class="login_out_label" align="right">Captcha text (below):</td>
752 @ <td><input type="text" id="cap" name="cap" value="" size="30" /></td>
753 @ </tr>
754 @ <tr><td></td>
755 @ <td><input type="submit" name="new" value="Register" /></td></tr>
756 @ </table>
757 @ <div class="captcha"><table class="captcha"><tr><td><pre>
758 @ %s(zCaptcha)
759 @ </pre></td></tr></table>
760 @ </form>
761 style_footer();
762
763 free(zCaptcha);
764 }
765
+137
--- src/login.c
+++ src/login.c
@@ -266,10 +266,14 @@
266266
}
267267
@ your user-id and password at the left and press the
268268
@ "Login" button. Your user name will be stored in a browser cookie.
269269
@ You must configure your web browser to accept cookies in order for
270270
@ the login to take.</p>
271
+ if( db_get_boolean("self-register", 0) ){
272
+ @ <p>If you do not have an account, you can
273
+ @ <a href="%s(g.zTop)/register?g=%T(P("G"))">create one</a>.
274
+ }
271275
if( zAnonPw ){
272276
unsigned int uSeed = captcha_seed();
273277
char const *zDecoded = captcha_decode(uSeed);
274278
int bAutoCaptcha = db_get_boolean("auto-captcha", 1);
275279
char *zCaptcha = captcha_render(zDecoded);
@@ -623,5 +627,138 @@
623627
g.okCsrf = 1;
624628
return;
625629
}
626630
fossil_fatal("Cross-site request forgery attempt");
627631
}
632
+
633
+/*
634
+** WEBPAGE: register
635
+**
636
+** Generate the register page.
637
+**
638
+*/
639
+void register_page(void){
640
+ const char *zUsername, *zPasswd, *zConfirm, *zContact, *zCS, *zPw, *zCap;
641
+ if( !db_get_boolean("self-register", 0) ){
642
+ style_header("Registration not possible");
643
+ @ <p>This project does not allow user self-registration. Please contact the
644
+ @ project administrator to obtain an account.</p>
645
+ style_footer();
646
+ return;
647
+ }
648
+
649
+ style_header("Register");
650
+ zUsername = P("u");
651
+ zPasswd = P("p");
652
+ zConfirm = P("cp");
653
+ zContact = P("c");
654
+ zCap = P("cap");
655
+ zCS = P("cs"); /* Captcha Secret */
656
+
657
+ /* Try to make any sense from user input. */
658
+ if( P("new") ){
659
+ if( zCS==0 ) fossil_redirect_home(); /* Forged request */
660
+ zPw = captcha_decode((unsigned int)atoi(zCS));
661
+ if( !(zUsername && zPasswd && zConfirm && zContact) ){
662
+ @ <p><span class="loginError">
663
+ @ All fields are obligatory.
664
+ @ </span></p>
665
+ }else if( strlen(zPasswd) < 6){
666
+ @ <p><span class="loginError">
667
+ @ Password too weak.
668
+ @ </span></p>
669
+ }else if( strcmp(zPasswd,zConfirm)!=0 ){
670
+ @ <p><span class="loginError">
671
+ @ The two copies of your new passwords do not match.
672
+ @ </span></p>
673
+ }else if( strcasecmp(zPw, zCap)!=0 ){
674
+ @ <p><span class="loginError">
675
+ @ Captcha text invalid.
676
+ @ </span></p>
677
+ }else{
678
+ /* This almost is stupid copy-paste of code from user.c:user_cmd(). */
679
+ Blob passwd, login, caps, contact;
680
+
681
+ blob_init(&login, zUsername, -1);
682
+ blob_init(&contact, zContact, -1);
683
+ blob_init(&caps, db_get("default-perms", "u"), -1);
684
+ blob_init(&passwd, zPasswd, -1);
685
+
686
+ if( db_exists("SELECT 1 FROM user WHERE login=%B", &login) ){
687
+ /* Here lies the reason I don't use zErrMsg - it would not substitute
688
+ * this %s(zUsername), or at least I don't know how to force it to.*/
689
+ @ <p><span class="loginError">
690
+ @ %s(zUsername) already exists.
691
+ @ </span></p>
692
+ }else{
693
+ char *zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login));
694
+ db_multi_exec(
695
+ "INSERT INTO user(login,pw,cap,info)"
696
+ "VALUES(%B,%Q,%B,%B)",
697
+ &login, zPw, &caps, &contact
698
+ );
699
+ free(zPw);
700
+
701
+ /* The user is registered, now just log him in. */
702
+ int uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUsername);
703
+ char *zCookie;
704
+ const char *zCookieName = login_cookie_name();
705
+ const char *zExpire = db_get("cookie-expire","8766");
706
+ int expires = atoi(zExpire)*3600;
707
+ const char *zIpAddr = PD("REMOTE_ADDR","nil");
708
+
709
+ zCookie = db_text(0, "SELECT '%d/' || hex(randomblob(25))", uid);
710
+ cgi_set_cookie(zCookieName, zCookie, 0, expires);
711
+ db_multi_exec(
712
+ "UPDATE user SET cookie=%Q, ipaddr=%Q, "
713
+ " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d",
714
+ zCookie, zIpAddr, expires, uid
715
+ );
716
+ redirect_to_g();
717
+
718
+ }
719
+ }
720
+ }
721
+
722
+ /* Prepare the captcha. */
723
+ unsigned int uSeed = captcha_seed();
724
+ char const *zDecoded = captcha_decode(uSeed);
725
+ char *zCaptcha = captcha_render(zDecoded);
726
+
727
+ /* Print out the registration form. */
728
+ @ <form action="register" method="post">
729
+ if( P("g") ){
730
+ @ <input type="hidden" name="g" value="%h(P("g"))" />
731
+ }
732
+ @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
733
+ @ <table class="login_out">
734
+ @ <tr>
735
+ @ <td class="login_out_label" align="right">User ID:</td>
736
+ @ <td><input type="text" id="u" name="u" value="" size="30" /></td>
737
+ @ </tr>
738
+ @ <tr>
739
+ @ <td class="login_out_label" align="right">Password:</td>
740
+ @ <td><input type="password" id="p" name="p" value="" size="30" /></td>
741
+ @ </tr>
742
+ @ <tr>
743
+ @ <td class="login_out_label" align="right">Confirm password:</td>
744
+ @ <td><input type="password" id="cp" name="cp" value="" size="30" /></td>
745
+ @ </tr>
746
+ @ <tr>
747
+ @ <td class="login_out_label" align="right">Contact info:</td>
748
+ @ <td><input type="text" id="c" name="c" value="" size="30" /></td>
749
+ @ </tr>
750
+ @ <tr>
751
+ @ <td class="login_out_label" align="right">Captcha text (below):</td>
752
+ @ <td><input type="text" id="cap" name="cap" value="" size="30" /></td>
753
+ @ </tr>
754
+ @ <tr><td></td>
755
+ @ <td><input type="submit" name="new" value="Register" /></td></tr>
756
+ @ </table>
757
+ @ <div class="captcha"><table class="captcha"><tr><td><pre>
758
+ @ %s(zCaptcha)
759
+ @ </pre></td></tr></table>
760
+ @ </form>
761
+ style_footer();
762
+
763
+ free(zCaptcha);
764
+}
628765
--- src/login.c
+++ src/login.c
@@ -266,10 +266,14 @@
266 }
267 @ your user-id and password at the left and press the
268 @ "Login" button. Your user name will be stored in a browser cookie.
269 @ You must configure your web browser to accept cookies in order for
270 @ the login to take.</p>
 
 
 
 
271 if( zAnonPw ){
272 unsigned int uSeed = captcha_seed();
273 char const *zDecoded = captcha_decode(uSeed);
274 int bAutoCaptcha = db_get_boolean("auto-captcha", 1);
275 char *zCaptcha = captcha_render(zDecoded);
@@ -623,5 +627,138 @@
623 g.okCsrf = 1;
624 return;
625 }
626 fossil_fatal("Cross-site request forgery attempt");
627 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
628
--- src/login.c
+++ src/login.c
@@ -266,10 +266,14 @@
266 }
267 @ your user-id and password at the left and press the
268 @ "Login" button. Your user name will be stored in a browser cookie.
269 @ You must configure your web browser to accept cookies in order for
270 @ the login to take.</p>
271 if( db_get_boolean("self-register", 0) ){
272 @ <p>If you do not have an account, you can
273 @ <a href="%s(g.zTop)/register?g=%T(P("G"))">create one</a>.
274 }
275 if( zAnonPw ){
276 unsigned int uSeed = captcha_seed();
277 char const *zDecoded = captcha_decode(uSeed);
278 int bAutoCaptcha = db_get_boolean("auto-captcha", 1);
279 char *zCaptcha = captcha_render(zDecoded);
@@ -623,5 +627,138 @@
627 g.okCsrf = 1;
628 return;
629 }
630 fossil_fatal("Cross-site request forgery attempt");
631 }
632
633 /*
634 ** WEBPAGE: register
635 **
636 ** Generate the register page.
637 **
638 */
639 void register_page(void){
640 const char *zUsername, *zPasswd, *zConfirm, *zContact, *zCS, *zPw, *zCap;
641 if( !db_get_boolean("self-register", 0) ){
642 style_header("Registration not possible");
643 @ <p>This project does not allow user self-registration. Please contact the
644 @ project administrator to obtain an account.</p>
645 style_footer();
646 return;
647 }
648
649 style_header("Register");
650 zUsername = P("u");
651 zPasswd = P("p");
652 zConfirm = P("cp");
653 zContact = P("c");
654 zCap = P("cap");
655 zCS = P("cs"); /* Captcha Secret */
656
657 /* Try to make any sense from user input. */
658 if( P("new") ){
659 if( zCS==0 ) fossil_redirect_home(); /* Forged request */
660 zPw = captcha_decode((unsigned int)atoi(zCS));
661 if( !(zUsername && zPasswd && zConfirm && zContact) ){
662 @ <p><span class="loginError">
663 @ All fields are obligatory.
664 @ </span></p>
665 }else if( strlen(zPasswd) < 6){
666 @ <p><span class="loginError">
667 @ Password too weak.
668 @ </span></p>
669 }else if( strcmp(zPasswd,zConfirm)!=0 ){
670 @ <p><span class="loginError">
671 @ The two copies of your new passwords do not match.
672 @ </span></p>
673 }else if( strcasecmp(zPw, zCap)!=0 ){
674 @ <p><span class="loginError">
675 @ Captcha text invalid.
676 @ </span></p>
677 }else{
678 /* This almost is stupid copy-paste of code from user.c:user_cmd(). */
679 Blob passwd, login, caps, contact;
680
681 blob_init(&login, zUsername, -1);
682 blob_init(&contact, zContact, -1);
683 blob_init(&caps, db_get("default-perms", "u"), -1);
684 blob_init(&passwd, zPasswd, -1);
685
686 if( db_exists("SELECT 1 FROM user WHERE login=%B", &login) ){
687 /* Here lies the reason I don't use zErrMsg - it would not substitute
688 * this %s(zUsername), or at least I don't know how to force it to.*/
689 @ <p><span class="loginError">
690 @ %s(zUsername) already exists.
691 @ </span></p>
692 }else{
693 char *zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login));
694 db_multi_exec(
695 "INSERT INTO user(login,pw,cap,info)"
696 "VALUES(%B,%Q,%B,%B)",
697 &login, zPw, &caps, &contact
698 );
699 free(zPw);
700
701 /* The user is registered, now just log him in. */
702 int uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUsername);
703 char *zCookie;
704 const char *zCookieName = login_cookie_name();
705 const char *zExpire = db_get("cookie-expire","8766");
706 int expires = atoi(zExpire)*3600;
707 const char *zIpAddr = PD("REMOTE_ADDR","nil");
708
709 zCookie = db_text(0, "SELECT '%d/' || hex(randomblob(25))", uid);
710 cgi_set_cookie(zCookieName, zCookie, 0, expires);
711 db_multi_exec(
712 "UPDATE user SET cookie=%Q, ipaddr=%Q, "
713 " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d",
714 zCookie, zIpAddr, expires, uid
715 );
716 redirect_to_g();
717
718 }
719 }
720 }
721
722 /* Prepare the captcha. */
723 unsigned int uSeed = captcha_seed();
724 char const *zDecoded = captcha_decode(uSeed);
725 char *zCaptcha = captcha_render(zDecoded);
726
727 /* Print out the registration form. */
728 @ <form action="register" method="post">
729 if( P("g") ){
730 @ <input type="hidden" name="g" value="%h(P("g"))" />
731 }
732 @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
733 @ <table class="login_out">
734 @ <tr>
735 @ <td class="login_out_label" align="right">User ID:</td>
736 @ <td><input type="text" id="u" name="u" value="" size="30" /></td>
737 @ </tr>
738 @ <tr>
739 @ <td class="login_out_label" align="right">Password:</td>
740 @ <td><input type="password" id="p" name="p" value="" size="30" /></td>
741 @ </tr>
742 @ <tr>
743 @ <td class="login_out_label" align="right">Confirm password:</td>
744 @ <td><input type="password" id="cp" name="cp" value="" size="30" /></td>
745 @ </tr>
746 @ <tr>
747 @ <td class="login_out_label" align="right">Contact info:</td>
748 @ <td><input type="text" id="c" name="c" value="" size="30" /></td>
749 @ </tr>
750 @ <tr>
751 @ <td class="login_out_label" align="right">Captcha text (below):</td>
752 @ <td><input type="text" id="cap" name="cap" value="" size="30" /></td>
753 @ </tr>
754 @ <tr><td></td>
755 @ <td><input type="submit" name="new" value="Register" /></td></tr>
756 @ </table>
757 @ <div class="captcha"><table class="captcha"><tr><td><pre>
758 @ %s(zCaptcha)
759 @ </pre></td></tr></table>
760 @ </form>
761 style_footer();
762
763 free(zCaptcha);
764 }
765
+16
--- src/setup.c
+++ src/setup.c
@@ -790,10 +790,26 @@
790790
@ <p>Fossil tries to limit out-bound sync, clone, and pull packets
791791
@ to this many bytes, uncompressed. If the client requires more data
792792
@ than this, then the client will issue multiple HTTP requests.
793793
@ Values below 1 million are not recommended. 5 million is a
794794
@ reasonable number.</p>
795
+
796
+ @ <hr />
797
+ onoff_attribute("Allow users to register themselves",
798
+ "self-register", "selfregister", 0);
799
+ @ <p>Allow users to register themselves through the HTTP UI.
800
+ @ The registration form always requires filling in a CAPTCHA
801
+ @ (<em>auto-captcha</em> setting is ignored). Still, bear in mind that anyone
802
+ @ can register under any user name. This option is useful for public projects
803
+ @ where you do not want everyone in any ticket discussion to be named
804
+ @ "Anonymous".</p>
805
+
806
+ @ <hr />
807
+ entry_attribute("Default privileges", 10, "default-perms", "defaultperms", "u");
808
+ @ <p>Permissions given to users that register themselves using the HTTP UI
809
+ @ or are registered by the administrator using the command line interface.
810
+ @ </p>
795811
796812
@ <hr />
797813
onoff_attribute("Show javascript button to fill in CAPTCHA",
798814
"auto-captcha", "autocaptcha", 0);
799815
@ <p>When enabled, a button appears on the login screen for user
800816
--- src/setup.c
+++ src/setup.c
@@ -790,10 +790,26 @@
790 @ <p>Fossil tries to limit out-bound sync, clone, and pull packets
791 @ to this many bytes, uncompressed. If the client requires more data
792 @ than this, then the client will issue multiple HTTP requests.
793 @ Values below 1 million are not recommended. 5 million is a
794 @ reasonable number.</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
795
796 @ <hr />
797 onoff_attribute("Show javascript button to fill in CAPTCHA",
798 "auto-captcha", "autocaptcha", 0);
799 @ <p>When enabled, a button appears on the login screen for user
800
--- src/setup.c
+++ src/setup.c
@@ -790,10 +790,26 @@
790 @ <p>Fossil tries to limit out-bound sync, clone, and pull packets
791 @ to this many bytes, uncompressed. If the client requires more data
792 @ than this, then the client will issue multiple HTTP requests.
793 @ Values below 1 million are not recommended. 5 million is a
794 @ reasonable number.</p>
795
796 @ <hr />
797 onoff_attribute("Allow users to register themselves",
798 "self-register", "selfregister", 0);
799 @ <p>Allow users to register themselves through the HTTP UI.
800 @ The registration form always requires filling in a CAPTCHA
801 @ (<em>auto-captcha</em> setting is ignored). Still, bear in mind that anyone
802 @ can register under any user name. This option is useful for public projects
803 @ where you do not want everyone in any ticket discussion to be named
804 @ "Anonymous".</p>
805
806 @ <hr />
807 entry_attribute("Default privileges", 10, "default-perms", "defaultperms", "u");
808 @ <p>Permissions given to users that register themselves using the HTTP UI
809 @ or are registered by the administrator using the command line interface.
810 @ </p>
811
812 @ <hr />
813 onoff_attribute("Show javascript button to fill in CAPTCHA",
814 "auto-captcha", "autocaptcha", 0);
815 @ <p>When enabled, a button appears on the login screen for user
816
+4 -3
--- src/user.c
+++ src/user.c
@@ -180,12 +180,13 @@
180180
if( g.argc<3 ){
181181
usage("capabilities|default|list|new|password ...");
182182
}
183183
n = strlen(g.argv[2]);
184184
if( n>=2 && strncmp(g.argv[2],"new",n)==0 ){
185
- Blob passwd, login, contact;
185
+ Blob passwd, login, caps, contact;
186186
char *zPw;
187
+ blob_init(&caps, db_get("default-perms", "u"), -1);
187188
188189
if( g.argc>=4 ){
189190
blob_init(&login, g.argv[3], -1);
190191
}else{
191192
prompt_user("login: ", &login);
@@ -204,12 +205,12 @@
204205
prompt_for_password("password: ", &passwd, 1);
205206
}
206207
zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login));
207208
db_multi_exec(
208209
"INSERT INTO user(login,pw,cap,info)"
209
- "VALUES(%B,%Q,'v',%B)",
210
- &login, zPw, &contact
210
+ "VALUES(%B,%Q,%B,%B)",
211
+ &login, zPw, &caps, &contact
211212
);
212213
free(zPw);
213214
}else if( n>=2 && strncmp(g.argv[2],"default",n)==0 ){
214215
user_select();
215216
if( g.argc==3 ){
216217
--- src/user.c
+++ src/user.c
@@ -180,12 +180,13 @@
180 if( g.argc<3 ){
181 usage("capabilities|default|list|new|password ...");
182 }
183 n = strlen(g.argv[2]);
184 if( n>=2 && strncmp(g.argv[2],"new",n)==0 ){
185 Blob passwd, login, contact;
186 char *zPw;
 
187
188 if( g.argc>=4 ){
189 blob_init(&login, g.argv[3], -1);
190 }else{
191 prompt_user("login: ", &login);
@@ -204,12 +205,12 @@
204 prompt_for_password("password: ", &passwd, 1);
205 }
206 zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login));
207 db_multi_exec(
208 "INSERT INTO user(login,pw,cap,info)"
209 "VALUES(%B,%Q,'v',%B)",
210 &login, zPw, &contact
211 );
212 free(zPw);
213 }else if( n>=2 && strncmp(g.argv[2],"default",n)==0 ){
214 user_select();
215 if( g.argc==3 ){
216
--- src/user.c
+++ src/user.c
@@ -180,12 +180,13 @@
180 if( g.argc<3 ){
181 usage("capabilities|default|list|new|password ...");
182 }
183 n = strlen(g.argv[2]);
184 if( n>=2 && strncmp(g.argv[2],"new",n)==0 ){
185 Blob passwd, login, caps, contact;
186 char *zPw;
187 blob_init(&caps, db_get("default-perms", "u"), -1);
188
189 if( g.argc>=4 ){
190 blob_init(&login, g.argv[3], -1);
191 }else{
192 prompt_user("login: ", &login);
@@ -204,12 +205,12 @@
205 prompt_for_password("password: ", &passwd, 1);
206 }
207 zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login));
208 db_multi_exec(
209 "INSERT INTO user(login,pw,cap,info)"
210 "VALUES(%B,%Q,%B,%B)",
211 &login, zPw, &caps, &contact
212 );
213 free(zPw);
214 }else if( n>=2 && strncmp(g.argv[2],"default",n)==0 ){
215 user_select();
216 if( g.argc==3 ){
217

Keyboard Shortcuts

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