5 # name: LJ::acid_encode
6 # des: Given a decimal number, returns base 30 encoding
7 # using an alphabet of letters & numbers that are
8 # not easily mistaken for each other.
9 # returns: Base 30 encoding, alwyas 7 characters long.
11 # des-number: Number to encode in base 30.
17 my $digits = "abcdefghjkmnpqrstvwxyz23456789";
20 $acid = substr($digits, $dig, 1) . $acid;
21 $num = ($num - $dig) / 30;
23 return ("a"x
(7-length($acid)) . $acid);
27 # name: LJ::acid_decode
28 # des: Given an acid encoding from [func[LJ::acid_encode]],
29 # returns the original decimal number.
32 # des-acid: base 30 number from [func[LJ::acid_encode]].
39 my $digits = "abcdefghjkmnpqrstvwxyz23456789";
40 for (0..30) { $val{substr($digits,$_,1)} = $_; }
44 return 0 unless ($acid =~ s/([$digits])$//o);
45 $num += $val{$1} * (30 ** $place++);
51 # name: LJ::acct_code_generate
52 # des: Creates invitation code(s) from an optional userid
54 # returns: Code generated (if quantity 1),
55 # number of codes generated (if quantity>1),
56 # or undef on failure.
57 # args: dbarg?, userid?, quantity?
58 # des-userid: Userid to make the invitation code from,
59 # else the code will be from userid 0 (system)
60 # des-quantity: Number of codes to generate (default 1)
62 sub acct_code_generate
65 my $userid = int(shift);
66 my $quantity = shift || 1;
68 my $dbh = LJ
::get_db_writer
();
70 my @authcodes = map {LJ
::make_auth_code
(5)} 1..$quantity;
71 my @values = map {"(NULL, $userid, 0, '$_')"} @authcodes;
72 my $sql = "INSERT INTO acctcode (acid, userid, rcptid, auth) "
73 . "VALUES " . join(",", @values);
74 my $num_rows = $dbh->do($sql) or return undef;
77 my $acid = $dbh->{'mysql_insertid'} or return undef;
78 return acct_code_encode
($acid, $authcodes[0]);
85 # name: LJ::acct_code_encode
86 # des: Given an account ID integer and a 5 digit auth code, returns
87 # a 12 digit account code.
88 # returns: 12 digit account code.
90 # des-acid: account ID, a 4 byte unsigned integer
91 # des-auth: 5 random characters from base 30 alphabet.
97 return lc($auth) . acid_encode
($acid);
101 # name: LJ::acct_code_decode
102 # des: Breaks an account code down into its two parts
103 # returns: list of (account ID, auth code)
105 # des-code: 12 digit account code
110 return (acid_decode
(substr($code, 5, 7)), lc(substr($code, 0, 5)));
114 # name: LJ::acct_code_check
115 # des: Checks the validity of a given account code
116 # returns: boolean; 0 on failure, 1 on validity. sets $$err on failure.
117 # args: dbarg?, code, err?, userid?
118 # des-code: account code to check
119 # des-err: optional scalar ref to put error message into on failure
120 # des-userid: optional userid which is allowed in the rcptid field,
121 # to allow for htdocs/create.bml case when people double
122 # click the submit button.
128 my $err = shift; # optional; scalar ref
129 my $userid = shift; # optional; acceptable userid (double-click proof)
131 my $dbh = LJ
::get_db_writer
();
133 unless (length($code) == 12) {
134 $$err = "Malformed code; not 12 characters.";
138 my ($acid, $auth) = acct_code_decode
($code);
140 my $ac = $dbh->selectrow_hashref("SELECT userid, rcptid, auth ".
141 "FROM acctcode WHERE acid=?",
144 unless ($ac && $ac->{'auth'} eq $auth) {
145 $$err = "Invalid account code.";
149 if ($ac->{'rcptid'} && $ac->{'rcptid'} != $userid) {
150 $$err = "This code has already been used: $code";
154 # is the journal this code came from suspended?
155 my $u = LJ
::load_userid
($ac->{'userid'});
156 if ($u && $u->{'statusvis'} eq "S") {
157 $$err = "Code belongs to a suspended account.";