diff options
-rw-r--r-- | src/sledjchisl/sledjchisl.c | 551 |
1 files changed, 288 insertions, 263 deletions
diff --git a/src/sledjchisl/sledjchisl.c b/src/sledjchisl/sledjchisl.c index 16876f0..b88b1a5 100644 --- a/src/sledjchisl/sledjchisl.c +++ b/src/sledjchisl/sledjchisl.c | |||
@@ -581,6 +581,28 @@ static char *getStrH(qhashtbl_t *hash, char *key) | |||
581 | } | 581 | } |
582 | 582 | ||
583 | 583 | ||
584 | static void bitch(reqData *Rd, char *message, char *log) | ||
585 | { | ||
586 | addStrL(Rd->errors, message); | ||
587 | E("%s %s %s - %s %s", getStrH(Rd->headers, "REMOTE_ADDR"), Rd->shs.UUID, Rd->shs.name, message, log); | ||
588 | } | ||
589 | |||
590 | /* "A token cookie that references a non-existent session, its value should be replaced immediately to prevent session fixation." | ||
591 | https://owasp.org/www-community/attacks/Session_fixation | ||
592 | Which describes the problem, but offers no solution. | ||
593 | See https://stackoverflow.com/questions/549/the-definitive-guide-to-form-based-website-authentication?rq=1. | ||
594 | I think this means send a new cookie. | ||
595 | I clear out the cookies and send blank ones with -1 maxAge, so they should get deleted. | ||
596 | */ | ||
597 | static void bitchSession(reqData *Rd, char *message, char *log) | ||
598 | { | ||
599 | if ('\0' != message[0]) | ||
600 | addStrL(Rd->errors, message); | ||
601 | C("%s %s %s - %s %s", getStrH(Rd->headers, "REMOTE_ADDR"), Rd->shs.UUID, Rd->shs.name, message, log); | ||
602 | Rd->shs.status = SHS_BOGUS; | ||
603 | } | ||
604 | |||
605 | |||
584 | char *myHMAC(char *in, boolean b64) | 606 | char *myHMAC(char *in, boolean b64) |
585 | { | 607 | { |
586 | EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); // Gets renamed to EVP_MD_CTX_new() in later versions. | 608 | EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); // Gets renamed to EVP_MD_CTX_new() in later versions. |
@@ -629,6 +651,272 @@ void PrintTable(lua_State *L) | |||
629 | } | 651 | } |
630 | } | 652 | } |
631 | 653 | ||
654 | typedef struct _qLua qLua; | ||
655 | struct _qLua | ||
656 | { | ||
657 | int type; | ||
658 | union | ||
659 | { | ||
660 | boolean b; | ||
661 | int i; | ||
662 | float f; | ||
663 | char *s; | ||
664 | qtreetbl_t *t; | ||
665 | } v; | ||
666 | }; | ||
667 | |||
668 | qLua *qLuaGet(qtreetbl_t *tree, char *key) | ||
669 | { | ||
670 | return (qLua *) tree->get(tree, key, NULL, false); | ||
671 | } | ||
672 | |||
673 | qtreetbl_t *lua2tree() | ||
674 | { | ||
675 | qtreetbl_t *ret = qtreetbl(0); | ||
676 | |||
677 | if (NULL != ret) | ||
678 | { | ||
679 | qLua q; | ||
680 | |||
681 | lua_pushnil(L); // +1 nil, first key | ||
682 | while(lua_next(L, -2) != 0) // -1 key, +2 next key, value (or 0 if nothing left in table) | ||
683 | { | ||
684 | // stack now contains: -1 => value; -2 => key; -3 => table | ||
685 | // copy the key so that lua_tostring does not modify the original | ||
686 | lua_pushvalue(L, -2); | ||
687 | // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table | ||
688 | char *n = (char *) lua_tostring(L, -1); // 0, modifies key copy | ||
689 | q.type = lua_type(L, -2); // 0 | ||
690 | // Numbers can convert to strings, so check for numbers before checking for strings. | ||
691 | // On the other hand, strings that can be converted to numbers also pass lua_isnumber(). sigh | ||
692 | switch(q.type) | ||
693 | { | ||
694 | case LUA_TBOOLEAN : {q.v.b = lua_toboolean(L, -2); break;} | ||
695 | case LUA_TINTEGER : {q.v.i = lua_tointeger(L, -2); break;} | ||
696 | case LUA_TNUMBER : {q.v.f = lua_tonumber(L, -2); break;} | ||
697 | case LUA_TSTRING : {q.v.s = (char *) lua_tostring(L, -2); break;} | ||
698 | case LUA_TTABLE : {lua_pushvalue(L, -2); q.v.t = lua2tree(); lua_pop(L, 1); break;} | ||
699 | |||
700 | default : | ||
701 | { | ||
702 | q.v.s = (char *) lua_tostring(L, -2); // 0 | ||
703 | E("Unknown Lua variable type for %s = %s is %d", n, q.v.s, q.type); | ||
704 | break; | ||
705 | } | ||
706 | } | ||
707 | ret->put(ret, n, &q, sizeof(qLua)); | ||
708 | // pop value + copy of key, leaving original key | ||
709 | lua_pop(L, 2); // -2 value and copy of key | ||
710 | // stack now contains: -1 => key; -2 => table | ||
711 | } | ||
712 | } | ||
713 | else | ||
714 | { | ||
715 | D("No memory left."); | ||
716 | perror_msg("Unable to allocate memory"); | ||
717 | } | ||
718 | |||
719 | return ret; | ||
720 | } | ||
721 | |||
722 | qtreetbl_t *Lua2tree(char *file, char *var) | ||
723 | { | ||
724 | qtreetbl_t *ret = NULL; | ||
725 | struct stat st; | ||
726 | |||
727 | if (0 != lstat(file, &st)) | ||
728 | { | ||
729 | D("No %s file.", file); | ||
730 | perror_msg("Unable to stat %s", file); | ||
731 | } | ||
732 | else | ||
733 | { | ||
734 | if (luaL_loadfile(L, file)) // +1 the chunk or an error message. If something went wrong, error message is at the top of the stack. | ||
735 | E("Couldn't load Lua file: %s", lua_tostring(L, -1)); // 0 | ||
736 | else | ||
737 | { | ||
738 | if (lua_pcall(L, 0, LUA_MULTRET, 0)) // +1 result or an error message. Except we get 0 or error? | ||
739 | { | ||
740 | E("Failed to run Lua script: %s", lua_tostring(L, -1)); // 0 | ||
741 | lua_pop(L, 1); // -1 chunk or error message | ||
742 | } | ||
743 | else | ||
744 | { | ||
745 | lua_getglobal(L, var); // +1 the value of var | ||
746 | ret = lua2tree(); | ||
747 | lua_pop(L, 1); // -1 var | ||
748 | } | ||
749 | } | ||
750 | lua_pop(L, 1); // -1 chunk or error message | ||
751 | } | ||
752 | return ret; | ||
753 | } | ||
754 | |||
755 | |||
756 | int Lua2hashtbl(char *file, qhashtbl_t *hash, char *var) | ||
757 | { | ||
758 | int ret = FALSE; | ||
759 | struct stat st; | ||
760 | |||
761 | if (0 != lstat(file, &st)) | ||
762 | { | ||
763 | D("No %s file.", file); | ||
764 | perror_msg("Unable to stat %s", file); | ||
765 | return FALSE; | ||
766 | } | ||
767 | else | ||
768 | { | ||
769 | int status = luaL_loadfile(L, file); // +1 the chunk or an error message. | ||
770 | if (status) // If something went wrong, error message is at the top of the stack. | ||
771 | E("Couldn't load Lua file: %s", lua_tostring(L, -1)); // 0 | ||
772 | else | ||
773 | { | ||
774 | int result = lua_pcall(L, 0, LUA_MULTRET, 0); // +1 result or an error message. Except we get 0 or error? | ||
775 | if (result) | ||
776 | { | ||
777 | E("Failed to run Lua script: %s", lua_tostring(L, -1)); // 0 | ||
778 | lua_pop(L, 1); // -1 chunk or error message | ||
779 | } | ||
780 | else | ||
781 | { | ||
782 | lua_getglobal(L, var); // +1 the value of var | ||
783 | lua_pushnil(L); // +1 nil, first key | ||
784 | while(lua_next(L, -2) != 0) // -1 key, +2 next key, value (or 0 if nothing left in table) | ||
785 | { | ||
786 | char *n = (char *) lua_tostring(L, -2); // 0 | ||
787 | |||
788 | // Numbers can convert to strings, so check for numbers before checking for strings. | ||
789 | // On the other hand, strings that can be converted to numbers also pass lua_isnumber(). sigh | ||
790 | if (lua_isnumber(L, -1)) // 0 | ||
791 | { | ||
792 | float v = lua_tonumber(L, -1); // 0 | ||
793 | hash->put(hash, n, &v, sizeof(float)); | ||
794 | } | ||
795 | else if (lua_isstring(L, -1)) // 0 | ||
796 | hash->putstr(hash, n, (char *) lua_tostring(L, -1)); // 0 | ||
797 | else if (lua_isboolean(L, -1)) // 0 | ||
798 | { | ||
799 | int v = lua_toboolean(L, -1); // 0 | ||
800 | hash->putint(hash, n, v); | ||
801 | } | ||
802 | else | ||
803 | { | ||
804 | char *v = (char *) lua_tostring(L, -1); // 0 | ||
805 | E("Unknown Lua variable type for %s = %s", n, v); | ||
806 | } | ||
807 | lua_pop(L, 1); // -1 value | ||
808 | } | ||
809 | lua_pop(L, 1); // -1 var | ||
810 | ret = TRUE; | ||
811 | } | ||
812 | } | ||
813 | } | ||
814 | lua_pop(L, 1); // -1 chunk or error message | ||
815 | return ret; | ||
816 | } | ||
817 | |||
818 | int LuaToHash(reqData *Rd, char *file, char *var, qhashtbl_t *tnm, int ret, struct stat *st, struct timespec *now, char *type) | ||
819 | { | ||
820 | struct timespec then; | ||
821 | |||
822 | if (-1 == clock_gettime(CLOCK_REALTIME, &then)) | ||
823 | perror_msg("Unable to get the time."); | ||
824 | I("Reading %s file %s", type, file); | ||
825 | if (!Lua2hashtbl(file, tnm, var)) | ||
826 | { | ||
827 | bitch(Rd, "Broken thing.", "Can't run file."); | ||
828 | ret++; | ||
829 | } | ||
830 | else | ||
831 | { | ||
832 | if (-1 == clock_gettime(CLOCK_REALTIME, now)) | ||
833 | perror_msg("Unable to get the time."); | ||
834 | double n = (now->tv_sec * 1000000000.0) + now->tv_nsec; | ||
835 | double t = (then.tv_sec * 1000000000.0) + then.tv_nsec; | ||
836 | T("Reading %s file took %lf seconds", type, (n - t) / 1000000000.0); | ||
837 | } | ||
838 | |||
839 | return ret; | ||
840 | } | ||
841 | |||
842 | |||
843 | boolean writeLuaDouble(reqData *Rd, int fd, char *file, char *name, double number) | ||
844 | { | ||
845 | boolean ret = TRUE; | ||
846 | // TODO - putting these in Lua as numbers causes lua_tolstring to barf when we read them. Though Lua is supposed to convert between numbers and strings. | ||
847 | char *t = xmprintf(" ['%s'] = '%f',\n", name, number); // NOTE - default precision is 6 decimal places. | ||
848 | size_t l = strlen(t); | ||
849 | |||
850 | if (l != writeall(fd, t, l)) | ||
851 | { | ||
852 | perror_msg("Writing %s", file); | ||
853 | ret = FALSE; | ||
854 | } | ||
855 | free(t); | ||
856 | return ret; | ||
857 | } | ||
858 | |||
859 | boolean writeLuaInteger(reqData *Rd, int fd, char *file, char *name, long number) | ||
860 | { | ||
861 | boolean ret = TRUE; | ||
862 | // TODO - putting these in Lua as numbers causes lua_tolstring to barf when we read them. Though Lua is supposed to convert between numbers and strings. | ||
863 | char *t = xmprintf(" ['%s'] = '%ld',\n", name, number); | ||
864 | size_t l = strlen(t); | ||
865 | |||
866 | if (l != writeall(fd, t, l)) | ||
867 | { | ||
868 | perror_msg("Writing %s", file); | ||
869 | ret = FALSE; | ||
870 | } | ||
871 | free(t); | ||
872 | return ret; | ||
873 | } | ||
874 | |||
875 | boolean writeLuaString(reqData *Rd, int fd, char *file, char *name, char *string) | ||
876 | { | ||
877 | boolean ret = TRUE; | ||
878 | |||
879 | if (NULL == string) | ||
880 | string = getStrH(Rd->stuff, name); | ||
881 | |||
882 | size_t l = strlen(string); | ||
883 | char *t0 = xmalloc(l * 2 + 1); | ||
884 | int i, j = 0; | ||
885 | |||
886 | // TODO - maybe escape other non printables as well? | ||
887 | for (i = 0; i < l; i++) | ||
888 | { | ||
889 | // We don't need to escape [] here, coz we are using '' below. Same applies to ", but do it anyway. | ||
890 | switch(string[i]) | ||
891 | { | ||
892 | case '\n': | ||
893 | case '\\': | ||
894 | case '\'': | ||
895 | case '"': | ||
896 | t0[j++] = '\\'; break; | ||
897 | } | ||
898 | if ('\n' == string[i]) | ||
899 | t0[j++] = 'n'; | ||
900 | else if ('\r' == string[i]) | ||
901 | ; | ||
902 | else | ||
903 | t0[j++] = string[i]; | ||
904 | } | ||
905 | t0[j] = '\0'; | ||
906 | |||
907 | char *t1 = xmprintf(" ['%s'] = '%s',\n", name, t0); | ||
908 | |||
909 | l = strlen(t1); | ||
910 | if (l != writeall(fd, t1, l)) | ||
911 | { | ||
912 | perror_msg("Writing %s to %s", name, file); | ||
913 | ret = FALSE; | ||
914 | } | ||
915 | free(t1); | ||
916 | free(t0); | ||
917 | return ret; | ||
918 | } | ||
919 | |||
632 | 920 | ||
633 | void doTmuxCmd(char *format, ...) | 921 | void doTmuxCmd(char *format, ...) |
634 | { | 922 | { |
@@ -3478,28 +3766,6 @@ https://stackoverflow.com/questions/16891729/best-practices-salting-peppering-pa | |||
3478 | qlisttbl_t *accountLevels = NULL; | 3766 | qlisttbl_t *accountLevels = NULL; |
3479 | 3767 | ||
3480 | 3768 | ||
3481 | static void bitch(reqData *Rd, char *message, char *log) | ||
3482 | { | ||
3483 | addStrL(Rd->errors, message); | ||
3484 | E("%s %s %s - %s %s", getStrH(Rd->headers, "REMOTE_ADDR"), Rd->shs.UUID, Rd->shs.name, message, log); | ||
3485 | } | ||
3486 | |||
3487 | /* "A token cookie that references a non-existent session, its value should be replaced immediately to prevent session fixation." | ||
3488 | https://owasp.org/www-community/attacks/Session_fixation | ||
3489 | Which describes the problem, but offers no solution. | ||
3490 | See https://stackoverflow.com/questions/549/the-definitive-guide-to-form-based-website-authentication?rq=1. | ||
3491 | I think this means send a new cookie. | ||
3492 | I clear out the cookies and send blank ones with -1 maxAge, so they should get deleted. | ||
3493 | */ | ||
3494 | static void bitchSession(reqData *Rd, char *message, char *log) | ||
3495 | { | ||
3496 | if ('\0' != message[0]) | ||
3497 | addStrL(Rd->errors, message); | ||
3498 | C("%s %s %s - %s %s", getStrH(Rd->headers, "REMOTE_ADDR"), Rd->shs.UUID, Rd->shs.name, message, log); | ||
3499 | Rd->shs.status = SHS_BOGUS; | ||
3500 | } | ||
3501 | |||
3502 | |||
3503 | // The ancient, insecure since 2011, Second Life / OpenSim password hashing algorithm. | 3769 | // The ancient, insecure since 2011, Second Life / OpenSim password hashing algorithm. |
3504 | char *newSLOSsalt(reqData *Rd) | 3770 | char *newSLOSsalt(reqData *Rd) |
3505 | { | 3771 | { |
@@ -3564,169 +3830,6 @@ t("checkSLOSpassword(%s, %s, %s, ", password, salt, passwordHash, fail); | |||
3564 | } | 3830 | } |
3565 | 3831 | ||
3566 | 3832 | ||
3567 | typedef struct _qLua qLua; | ||
3568 | struct _qLua | ||
3569 | { | ||
3570 | int type; | ||
3571 | union | ||
3572 | { | ||
3573 | boolean b; | ||
3574 | int i; | ||
3575 | float f; | ||
3576 | char *s; | ||
3577 | qtreetbl_t *t; | ||
3578 | } v; | ||
3579 | }; | ||
3580 | |||
3581 | qLua *qLuaGet(qtreetbl_t *tree, char *key) | ||
3582 | { | ||
3583 | return (qLua *) tree->get(tree, key, NULL, false); | ||
3584 | } | ||
3585 | |||
3586 | qtreetbl_t *lua2tree() | ||
3587 | { | ||
3588 | qtreetbl_t *ret = qtreetbl(0); | ||
3589 | |||
3590 | if (NULL != ret) | ||
3591 | { | ||
3592 | qLua q; | ||
3593 | |||
3594 | lua_pushnil(L); // +1 nil, first key | ||
3595 | while(lua_next(L, -2) != 0) // -1 key, +2 next key, value (or 0 if nothing left in table) | ||
3596 | { | ||
3597 | char *n = (char *) lua_tostring(L, -2); // 0 | ||
3598 | |||
3599 | q.type = lua_type(L, -1); // 0 | ||
3600 | // Numbers can convert to strings, so check for numbers before checking for strings. | ||
3601 | // On the other hand, strings that can be converted to numbers also pass lua_isnumber(). sigh | ||
3602 | switch(q.type) | ||
3603 | { | ||
3604 | case LUA_TBOOLEAN : {q.v.b = lua_toboolean(L, -1); break;} | ||
3605 | case LUA_TINTEGER : {q.v.i = lua_tointeger(L, -1); break;} | ||
3606 | case LUA_TNUMBER : {q.v.f = lua_tonumber(L, -1); break;} | ||
3607 | case LUA_TSTRING : {q.v.s = (char *) lua_tostring(L, -1); break;} | ||
3608 | case LUA_TTABLE : {q.v.t = lua2tree(); break;} | ||
3609 | |||
3610 | default : | ||
3611 | { | ||
3612 | q.v.s = (char *) lua_tostring(L, -1); // 0 | ||
3613 | E("Unknown Lua variable type for %s = %s is %d", n, q.v.s, q.type); | ||
3614 | break; | ||
3615 | } | ||
3616 | } | ||
3617 | ret->put(ret, n, &q, sizeof(qLua)); | ||
3618 | lua_pop(L, 1); // -1 value | ||
3619 | } | ||
3620 | } | ||
3621 | else | ||
3622 | { | ||
3623 | D("No memory left."); | ||
3624 | perror_msg("Unable to allocate memory"); | ||
3625 | } | ||
3626 | |||
3627 | return ret; | ||
3628 | } | ||
3629 | |||
3630 | qtreetbl_t *Lua2tree(char *file, char *var) | ||
3631 | { | ||
3632 | qtreetbl_t *ret = NULL; | ||
3633 | struct stat st; | ||
3634 | |||
3635 | if (0 != lstat(file, &st)) | ||
3636 | { | ||
3637 | D("No %s file.", file); | ||
3638 | perror_msg("Unable to stat %s", file); | ||
3639 | } | ||
3640 | else | ||
3641 | { | ||
3642 | if (luaL_loadfile(L, file)) // +1 the chunk or an error message. If something went wrong, error message is at the top of the stack. | ||
3643 | E("Couldn't load Lua file: %s", lua_tostring(L, -1)); // 0 | ||
3644 | else | ||
3645 | { | ||
3646 | if (lua_pcall(L, 0, LUA_MULTRET, 0)) // +1 result or an error message. Except we get 0 or error? | ||
3647 | { | ||
3648 | E("Failed to run Lua script: %s", lua_tostring(L, -1)); // 0 | ||
3649 | lua_pop(L, 1); // -1 chunk or error message | ||
3650 | } | ||
3651 | else | ||
3652 | { | ||
3653 | lua_getglobal(L, var); // +1 the value of var | ||
3654 | ret = lua2tree(); | ||
3655 | lua_pop(L, 1); // -1 var | ||
3656 | } | ||
3657 | } | ||
3658 | lua_pop(L, 1); // -1 chunk or error message | ||
3659 | } | ||
3660 | return ret; | ||
3661 | } | ||
3662 | |||
3663 | |||
3664 | int LuaToHash(reqData *Rd, char *file, char *var, qhashtbl_t *tnm, int ret, struct stat *st, struct timespec *now, char *type) | ||
3665 | { | ||
3666 | struct timespec then; | ||
3667 | |||
3668 | if (-1 == clock_gettime(CLOCK_REALTIME, &then)) | ||
3669 | perror_msg("Unable to get the time."); | ||
3670 | I("Reading %s file %s", type, file); | ||
3671 | if (0 != stat(file, st)) | ||
3672 | { | ||
3673 | D("No %s file.", file); | ||
3674 | perror_msg("Unable to stat %s", file); | ||
3675 | ret++; | ||
3676 | } | ||
3677 | else | ||
3678 | { | ||
3679 | int status = luaL_loadfile(Rd->L, file), result; | ||
3680 | |||
3681 | if (status) | ||
3682 | { | ||
3683 | bitch(Rd, "No such thing.", "Can't load file."); | ||
3684 | E("Couldn't load file: %s", lua_tostring(Rd->L, -1)); | ||
3685 | ret++; | ||
3686 | } | ||
3687 | else | ||
3688 | { | ||
3689 | result = lua_pcall(Rd->L, 0, LUA_MULTRET, 0); | ||
3690 | |||
3691 | if (result) | ||
3692 | { | ||
3693 | bitch(Rd, "Broken thing.", "Can't run file."); | ||
3694 | E("Failed to run script: %s", lua_tostring(Rd->L, -1)); | ||
3695 | ret++; | ||
3696 | } | ||
3697 | else | ||
3698 | { | ||
3699 | lua_getglobal(Rd->L, var); | ||
3700 | lua_pushnil(Rd->L); | ||
3701 | |||
3702 | while(lua_next(Rd->L, -2) != 0) | ||
3703 | { | ||
3704 | char *n = (char *) lua_tostring(Rd->L, -2); | ||
3705 | |||
3706 | if (lua_isstring(Rd->L, -1)) | ||
3707 | { | ||
3708 | tnm->putstr(tnm, n, (char *) lua_tostring(Rd->L, -1)); | ||
3709 | d("Lua reading (%s) %s = %s", type, n, getStrH(tnm, n)); | ||
3710 | } | ||
3711 | else | ||
3712 | { | ||
3713 | char *v = (char *) lua_tostring(Rd->L, -1); | ||
3714 | W("Unknown Lua variable type for %s = %s", n, v); | ||
3715 | } | ||
3716 | lua_pop(Rd->L, 1); | ||
3717 | } | ||
3718 | |||
3719 | if (-1 == clock_gettime(CLOCK_REALTIME, now)) | ||
3720 | perror_msg("Unable to get the time."); | ||
3721 | double n = (now->tv_sec * 1000000000.0) + now->tv_nsec; | ||
3722 | double t = (then.tv_sec * 1000000000.0) + then.tv_nsec; | ||
3723 | T("Reading %s file took %lf seconds", type, (n - t) / 1000000000.0); | ||
3724 | } | ||
3725 | } | ||
3726 | } | ||
3727 | |||
3728 | return ret; | ||
3729 | } | ||
3730 | 3833 | ||
3731 | 3834 | ||
3732 | char *checkLinky(reqData *Rd) | 3835 | char *checkLinky(reqData *Rd) |
@@ -4113,84 +4216,6 @@ systemFolders sysFolders[] = | |||
4113 | {"Trash", 14}, | 4216 | {"Trash", 14}, |
4114 | {NULL, -1} | 4217 | {NULL, -1} |
4115 | }; | 4218 | }; |
4116 | |||
4117 | boolean writeLuaDouble(reqData *Rd, int fd, char *file, char *name, double number) | ||
4118 | { | ||
4119 | boolean ret = TRUE; | ||
4120 | // TODO - putting these in Lua as numbers causes lua_tolstring to barf when we read them. Though Lua is supposed to convert between numbers and strings. | ||
4121 | char *t = xmprintf(" ['%s'] = '%f',\n", name, number); // NOTE - default precision is 6 decimal places. | ||
4122 | size_t l = strlen(t); | ||
4123 | |||
4124 | if (l != writeall(fd, t, l)) | ||
4125 | { | ||
4126 | perror_msg("Writing %s", file); | ||
4127 | ret = FALSE; | ||
4128 | } | ||
4129 | free(t); | ||
4130 | return ret; | ||
4131 | } | ||
4132 | |||
4133 | boolean writeLuaInteger(reqData *Rd, int fd, char *file, char *name, long number) | ||
4134 | { | ||
4135 | boolean ret = TRUE; | ||
4136 | // TODO - putting these in Lua as numbers causes lua_tolstring to barf when we read them. Though Lua is supposed to convert between numbers and strings. | ||
4137 | char *t = xmprintf(" ['%s'] = '%ld',\n", name, number); | ||
4138 | size_t l = strlen(t); | ||
4139 | |||
4140 | if (l != writeall(fd, t, l)) | ||
4141 | { | ||
4142 | perror_msg("Writing %s", file); | ||
4143 | ret = FALSE; | ||
4144 | } | ||
4145 | free(t); | ||
4146 | return ret; | ||
4147 | } | ||
4148 | |||
4149 | boolean writeLuaString(reqData *Rd, int fd, char *file, char *name, char *string) | ||
4150 | { | ||
4151 | boolean ret = TRUE; | ||
4152 | |||
4153 | if (NULL == string) | ||
4154 | string = getStrH(Rd->stuff, name); | ||
4155 | |||
4156 | size_t l = strlen(string); | ||
4157 | char *t0 = xmalloc(l * 2 + 1); | ||
4158 | int i, j = 0; | ||
4159 | |||
4160 | // TODO - maybe escape other non printables as well? | ||
4161 | for (i = 0; i < l; i++) | ||
4162 | { | ||
4163 | // We don't need to escape [] here, coz we are using '' below. Same applies to ", but do it anyway. | ||
4164 | switch(string[i]) | ||
4165 | { | ||
4166 | case '\n': | ||
4167 | case '\\': | ||
4168 | case '\'': | ||
4169 | case '"': | ||
4170 | t0[j++] = '\\'; break; | ||
4171 | } | ||
4172 | if ('\n' == string[i]) | ||
4173 | t0[j++] = 'n'; | ||
4174 | else if ('\r' == string[i]) | ||
4175 | ; | ||
4176 | else | ||
4177 | t0[j++] = string[i]; | ||
4178 | } | ||
4179 | t0[j] = '\0'; | ||
4180 | |||
4181 | char *t1 = xmprintf(" ['%s'] = '%s',\n", name, t0); | ||
4182 | |||
4183 | l = strlen(t1); | ||
4184 | if (l != writeall(fd, t1, l)) | ||
4185 | { | ||
4186 | perror_msg("Writing %s to %s", name, file); | ||
4187 | ret = FALSE; | ||
4188 | } | ||
4189 | free(t1); | ||
4190 | free(t0); | ||
4191 | return ret; | ||
4192 | } | ||
4193 | |||
4194 | static void accountWrite(reqData *Rd) | 4219 | static void accountWrite(reqData *Rd) |
4195 | { | 4220 | { |
4196 | char *uuid = getStrH(Rd->database, "UserAccounts.PrincipalID"); | 4221 | char *uuid = getStrH(Rd->database, "UserAccounts.PrincipalID"); |