| File: | lib/OpenAPI/Handler/Captcha.pm |
| Coverage: | 84.5% |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | package OpenAPI; | ||||||
| 2 | |||||||
| 3 | #use Smart::Comments; | ||||||
| 4 | 22 22 22 | 190 69 228 | use strict; | ||||
| 5 | 22 22 22 | 274 78 198 | use warnings; | ||||
| 6 | |||||||
| 7 | 22 22 22 | 270 71 222 | use vars qw( $UUID $Cache ); | ||||
| 8 | 22 22 22 | 306 96 282 | use GD::SecurityImage; | ||||
| 9 | 22 22 22 | 9476 88 254 | use utf8; | ||||
| 10 | 22 22 22 | 255 70 379 | use Encode 'encode'; | ||||
| 11 | |||||||
| 12 | my @CnWordList = qw( | ||||||
| 13 | 一本正经 上升 ‹降 不假思索 专门 | ||||||
| 14 | 严严实实 严寒 丰收 乌龟 乱成一› | ||||||
| 15 | 争奇斗艳 五光十色 交错 人行道 仁爱 | ||||||
| 16 | 仍然 仙子 代表 仰› 仰起 | ||||||
| 17 | 价值 传授 传播 似乎 低头 | ||||||
| 18 | 体贴 使劲 依然 保留 修建 | ||||||
| 19 | 修理 假装 傲慢 光洁 免得 | ||||||
| 20 | 全部 关系 兴‹ 兴趣 兴高采烈 | ||||||
| 21 | 冰‹ 冲击› 决心 准备 准确无误 | ||||||
| 22 | 凉爽 减少 减轻 几乎 凤尾竹 | ||||||
| 23 | ›举 ›造 前夕 前爪 ›量 | ||||||
| 24 | 加紧 动听 ‹敢 ‹气 包‹ | ||||||
| 25 | 千呼万唤 千奇百怪 卡片 危险 历史 | ||||||
| 26 | 参加 又松又软 双龙戏珠 古老 可口 | ||||||
| 27 | 可惜 台阶 各种各样 合二为一 合‹ | ||||||
| 28 | 同情 名堂 名贵 咱们 品行 | ||||||
| 29 | ››肢 ››脚朝天 ›首 ›然 ›案 | ||||||
| 30 | 地质学家 坚› 坦‹ 坪坝 垂头丧气 | ||||||
| 31 | 大惊失色 大显神威 大概 大腿 大致 | ||||||
| 32 | 奇怪 ‹› 奔流不息 奔跑 好奇 | ||||||
| 33 | 如实 姿势 威武 娇嫩 嫩绿 | ||||||
| 34 | 孔›舞 学问 宇宙 定时 宝贵 | ||||||
| 35 | 实验 宽裕 密切 密密层层 寻找 | ||||||
| 36 | 居然 展示 希› 平息 平整 | ||||||
| 37 | 引人注› 当初 形状 微生物 心意 | ||||||
| 38 | 忽然 恼怒 悄悄 情况 情绪 | ||||||
| 39 | 惊讶 愿意 慢吞吞 懂得 懒‹‹ | ||||||
| 40 | 成功 成果 成群结队 或者 战场 | ||||||
| 41 | 所以 扇子 ‹掌 ‹锯 才干 | ||||||
| 42 | 打扮 打量 抖动 抢走 披甲 | ||||||
| 43 | 抽出 ‹心 ‹›呼 ‹›引 ‹›架 | ||||||
| 44 | ‹访 ‹抱 ‹命 持久 按照 | ||||||
| 45 | 挡住 挤来挤去 捉迷藏 掌声 推动 | ||||||
| 46 | 推‹ 提醒 摆弄 摇晃 收藏 | ||||||
| 47 | 放大镜 敌人 教育家 散步 敬礼 | ||||||
| 48 | 敬重 文静 斧头 旅行 无论 | ||||||
| 49 | 昆虫 显微镜 显然 普通话 暗示 | ||||||
| 50 | 有趣 本能 朴素 杂志社 杏黄 | ||||||
| 51 | 村子 杨树 果然 柿子 栏板 | ||||||
| 52 | 检查 植物学家 横跨 欢唱 欢快 | ||||||
| 53 | 欢蹦乱跳 欣赏 止境 气味 气息 | ||||||
| 54 | 汇成 油亮亮 沿途 注视 洁白 | ||||||
| 55 | 浪费 深蓝 清凉 清闲 渔业工人 | ||||||
| 56 | 游戏 湿度 ‹润 激动 炎热 | ||||||
| 57 | 炮口 热烈 热闹 照›机 爬山 | ||||||
| 58 | 物产丰富 猜‹ 献出 玩具 玩意 | ||||||
| 59 | 玩耍 理会 瓶子 甜蜜 留心 | ||||||
| 60 | 留意 白发‹‹ ››开 ›提并论 ›距 | ||||||
| 61 | ›› ‹守 眼镜 石栏 研究 | ||||||
| 62 | 确确实实 磨坊 祖祖辈辈 祝福 神气 | ||||||
| 63 | 秘书 秦岭 穿戴 突然 ‹刻 | ||||||
| 64 | ‹即 第七课 等候 简单 算术 | ||||||
| 65 | 粗壮 粮食 精心 精美 紧张 | ||||||
| 66 | 纪念 纳闷 纸‹ 细微 终于 | ||||||
| 67 | 绒› 给予 继续 绳子 美观 | ||||||
| 68 | 考察 肌肤 肥料 肯定 胜利者 | ||||||
| 69 | 胶卷 胸脯 自卫 自言自语 舌头 | ||||||
| 70 | 艳丽 节省 芬芳迷人 花瓣 ‹醒 | ||||||
| 71 | 茂密 茂›› 茶杯 荒凉 药材 | ||||||
| 72 | 获得 菠萝 著名 蝴蝶 血液 | ||||||
| 73 | 观察 视线 记忆› 记者 讲述 | ||||||
| 74 | 设计 证明 试探 诚实 请教 | ||||||
| 75 | 调节 谦虚 超常 路途 躲闪 | ||||||
| 76 | 转告 转来转去 轮流 辫子 辽阔 | ||||||
| 77 | 迎候 这› ›攻 远近闻名 迷失 | ||||||
| 78 | 适宜 适应 遗产 遗迹 遥› | ||||||
| 79 | 遥远 邮票 郊外 重量 钓鱼 | ||||||
| 80 | 铜钟 镜片 长处 长› 阅读 | ||||||
| 81 | 阻› 陆续 陌生 随便 随意 | ||||||
| 82 | 难过 ›伟 ›合 需要 震惊 | ||||||
| 83 | 面包渣 顶峰 顺利 颜色 风尘仆仆 | ||||||
| 84 | 风景优美 飘扬 飘飘摇摇 飞散 飞舞 | ||||||
| 85 | 首次 香甜 骄傲 高低不平 鲜嫩 | ||||||
| 86 | 黑暗 鼓励 | ||||||
| 87 | ); | ||||||
| 88 | |||||||
| 89 | my @WordList = qw( | ||||||
| 90 | about afraid after again against | ||||||
| 91 | agree almost along angry another | ||||||
| 92 | answers arms around away back | ||||||
| 93 | ball basket become begin better | ||||||
| 94 | boat boating books booth born | ||||||
| 95 | both boxes boys bread brush | ||||||
| 96 | burn buses busy cake call | ||||||
| 97 | camping capital care careful carry | ||||||
| 98 | cars catch centre cheaper chess | ||||||
| 99 | china cinema city class clean | ||||||
| 100 | clever coat cold college comb | ||||||
| 101 | come country course cups dark | ||||||
| 102 | days decide declare deed deliver | ||||||
| 103 | develop dinner dirty doctor doing | ||||||
| 104 | door down dress drive each | ||||||
| 105 | early east eight eleven enjoy | ||||||
| 106 | evening every exam excited excuse | ||||||
| 107 | fall family famous fast faster | ||||||
| 108 | father feel fever fifty film | ||||||
| 109 | fine finish first fish fishing | ||||||
| 110 | five floor food foot forty | ||||||
| 111 | four friend friends from front | ||||||
| 112 | full funny future games give | ||||||
| 113 | glad goal good goodbye grade | ||||||
| 114 | ground grow hair half hand | ||||||
| 115 | hands happen hard have head | ||||||
| 116 | healthy hear heavily help here | ||||||
| 117 | high hill history hold hole | ||||||
| 118 | home hour hours house hundred | ||||||
| 119 | hungry hurry hurt idea instead | ||||||
| 120 | into invite jump just keep | ||||||
| 121 | kind kinds knock know ladder | ||||||
| 122 | lake largest last late learn | ||||||
| 123 | leave left lesson lessons letter | ||||||
| 124 | letters life lights like listen | ||||||
| 125 | little live long longer look | ||||||
| 126 | lunch machine make many market | ||||||
| 127 | match matter meals medical meeting | ||||||
| 128 | message middle million minute model | ||||||
| 129 | modern moment money month more | ||||||
| 130 | morning most move much music | ||||||
| 131 | name near nearly need news | ||||||
| 132 | next nice night nine north | ||||||
| 133 | number office once oneself only | ||||||
| 134 | open other over pair papers | ||||||
| 135 | parents parking party pass past | ||||||
| 136 | people piano pick piece place | ||||||
| 137 | plane plant play player plenty | ||||||
| 138 | points post prepare primary promise | ||||||
| 139 | pulling quarter quick quietly race | ||||||
| 140 | rain read ready rest result | ||||||
| 141 | return right road room roses | ||||||
| 142 | round rules rush school scoot | ||||||
| 143 | send seven several ship shirt | ||||||
| 144 | shoes shoot short show shower | ||||||
| 145 | side sides signs singing sixty | ||||||
| 146 | skate skating slim slow slowly | ||||||
| 147 | smile snowman some soon sorry | ||||||
| 148 | south spare speak sports square | ||||||
| 149 | squares stamps stand start station | ||||||
| 150 | stay stop store story street | ||||||
| 151 | student studies study such summer | ||||||
| 152 | supper table take talk tall | ||||||
| 153 | teach teacher team teeth tell | ||||||
| 154 | test thank that them then | ||||||
| 155 | there these thin thing things | ||||||
| 156 | think thirty this three through | ||||||
| 157 | ticket time times today traffic | ||||||
| 158 | train tree trees trip trouble | ||||||
| 159 | turn twenty twice under until | ||||||
| 160 | very view visit voice wait | ||||||
| 161 | wake walk wall want wash | ||||||
| 162 | washing watch ways wear week | ||||||
| 163 | weeks welcome well west what | ||||||
| 164 | when window winter wish with | ||||||
| 165 | word work world worried write | ||||||
| 166 | wrong year your hello moon | ||||||
| 167 | ); | ||||||
| 168 | |||||||
| 169 | # Create a normal image | ||||||
| 170 | sub GET_captcha_column { | ||||||
| 171 | 9 | 0 | 53 | my ($self, $bits) = @_; | |||
| 172 | 9 | 52 | my $col = $bits->[1]; | ||||
| 173 | 9 | 68 | if ($col eq 'id') { | ||||
| 174 | 9 | 59 | my $captcha_from_cookie = $self->{_captcha_from_cookie}; | ||||
| 175 | 9 | 58 | if ($captcha_from_cookie) { | ||||
| 176 | 8 | 87 | $OpenAPI::Cache->remove($captcha_from_cookie); | ||||
| 177 | } | ||||||
| 178 | |||||||
| 179 | 9 | 1331 | my $id = $UUID->create_str; | ||||
| 180 | 9 | 85 | $Cache->set($id => 1); | ||||
| 181 | |||||||
| 182 | 9 | 2019 | $self->{_cookie} = { captcha => $id }; | ||||
| 183 | 9 | 90 | return $id; | ||||
| 184 | } else { | ||||||
| 185 | 0 | 0 | die "Unknown captcha column: $col\n"; | ||||
| 186 | } | ||||||
| 187 | } | ||||||
| 188 | |||||||
| 189 | sub GET_captcha_value { | ||||||
| 190 | 15 | 0 | 98 | my ($self, $bits) = @_; | |||
| 191 | 15 | 87 | my $col = $bits->[1]; | ||||
| 192 | 15 | 82 | my $value = $bits->[2]; | ||||
| 193 | |||||||
| 194 | 15 | 62 | my $ext = 'gif'; | ||||
| 195 | 15 | 142 | if ($value =~ s/\.(gif|jpg|png|jpeg)$//g) { | ||||
| 196 | 6 | 40 | $ext = $1; | ||||
| 197 | 6 0 | 49 0 | if ($ext eq 'jpg') { $ext = 'jpeg' } | ||||
| 198 | } | ||||||
| 199 | 15 | 98 | if ($col eq 'id') { | ||||
| 200 | 15 | 1150 | my $id = $value; | ||||
| 201 | 15 | 152 | my $solution = $Cache->get($id); | ||||
| 202 | 15 | 568 | if (defined $solution) { | ||||
| 203 | 12 | 124 | my $lang = lc($self->{_cgi}->url_param('lang')) || 'en'; | ||||
| 204 | 12 | 117 | if ($lang eq 'cn') { | ||||
| 205 | #if ($solution eq '1') { # new ID, no solution yet | ||||||
| 206 | 4 | 31 | $solution = $self->gen_cn_solution; | ||||
| 207 | 4 | 32 | $self->gen_cn_image($solution); | ||||
| 208 | } elsif ($lang eq 'en') { | ||||||
| 209 | 7 | 46 | $solution = $self->gen_en_solution; | ||||
| 210 | 7 | 61 | $self->gen_en_image($solution); | ||||
| 211 | } else { | ||||||
| 212 | 1 | 4 | die "Unsupported lang (only cn and en allowed): $lang\n"; | ||||
| 213 | } | ||||||
| 214 | 11 | 523 | $Cache->set($id => $solution); | ||||
| 215 | 11 | 2561 | return; | ||||
| 216 | } else { | ||||||
| 217 | 3 | 11 | die "Invalid captcha ID: $id\n"; | ||||
| 218 | } | ||||||
| 219 | } else { | ||||||
| 220 | 0 | 0 | die "Unknown captcha column: $col\n"; | ||||
| 221 | } | ||||||
| 222 | } | ||||||
| 223 | |||||||
| 224 | sub gen_en_solution { | ||||||
| 225 | 7 | 0 | 42 | my ($self) = @_; | |||
| 226 | 7 | 28 | my $str = ''; | ||||
| 227 | 7 | 38 | my $list = \@WordList; | ||||
| 228 | 7 | 40 | my ($i, $j) = (0, 0); | ||||
| 229 | 7 | 50 | while ($i < 2) { | ||||
| 230 | 14 | 81 | last if $j > 100; | ||||
| 231 | 14 | 125 | my $rand = int rand scalar(@$list); | ||||
| 232 | 14 | 55 | my $saved_str = $str; | ||||
| 233 | 14 | 101 | $str .= $list->[$rand] . " "; | ||||
| 234 | 14 | 55 | my $len = length($str); | ||||
| 235 | 14 | 80 | if ($len >= 15) { | ||||
| 236 | 0 | 0 | $str = $saved_str; | ||||
| 237 | 0 | 0 | $j++; | ||||
| 238 | 0 | 0 | next; | ||||
| 239 | } | ||||||
| 240 | 14 | 106 | $i++; | ||||
| 241 | } | ||||||
| 242 | 7 | 47 | $str; | ||||
| 243 | # XXX debug only | ||||||
| 244 | } | ||||||
| 245 | |||||||
| 246 | sub gen_cn_solution { | ||||||
| 247 | 4 | 0 | 28 | my ($self) = @_; | |||
| 248 | 4 | 19 | my $str = ''; | ||||
| 249 | 4 | 23 | my $list = \@CnWordList; | ||||
| 250 | 4 | 29 | my ($i, $j) = (0, 0); | ||||
| 251 | 4 | 36 | while ($i < 2) { | ||||
| 252 | 7 | 48 | last if $j > 100; | ||||
| 253 | 7 | 54 | my $rand = int rand scalar(@$list); | ||||
| 254 | 7 | 33 | my $saved_str = $str; | ||||
| 255 | 7 | 50 | $str .= $list->[$rand]; | ||||
| 256 | 7 | 44 | my $len = length($str); | ||||
| 257 | 7 | 50 | last if $len == 3; | ||||
| 258 | 6 | 42 | if ($len >= 5) { | ||||
| 259 | 0 | 0 | $str = $saved_str; | ||||
| 260 | 0 | 0 | $j++; | ||||
| 261 | 0 | 0 | next; | ||||
| 262 | } | ||||||
| 263 | 6 | 58 | $i++; | ||||
| 264 | } | ||||||
| 265 | 4 | 33 | $str; | ||||
| 266 | # XXX debug only | ||||||
| 267 | } | ||||||
| 268 | |||||||
| 269 | sub gen_cn_image { | ||||||
| 270 | 4 | 0 | 28 | my ($self, $str) = @_; | |||
| 271 | 4 | 26 | my $angle = int rand 4; | ||||
| 272 | 4 | 92 | my $captcha = GD::SecurityImage->new( | ||||
| 273 | width => 100, | ||||||
| 274 | height => 37, | ||||||
| 275 | lines => 2 + int rand 2, | ||||||
| 276 | font => "$FindBin::Bin/../font/wqy-zenhei.ttf", | ||||||
| 277 | #thickness => 0.5, | ||||||
| 278 | rndmax => 3, | ||||||
| 279 | angle => $angle, | ||||||
| 280 | ptsize => 15, | ||||||
| 281 | #send_ctobg => 1, | ||||||
| 282 | #scramble => 1, | ||||||
| 283 | ); | ||||||
| 284 | |||||||
| 285 | #warn $str; | ||||||
| 286 | 4 | 4226 | $captcha->random($str); | ||||
| 287 | 4 | 221 | $captcha->create(ttf => 'default'); | ||||
| 288 | 4 | 3411 | die "Failed to load ttf font for GD: $@\n" if $captcha->gdbox_empty; | ||||
| 289 | 4 | 90 | $captcha->particle(300); # : 1732); | ||||
| 290 | 4 | 133700 | my ($image_data, $mime_type) = $captcha->out(compress => 1); | ||||
| 291 | 4 | 2769 | $self->{_bin_data} = $image_data; | ||||
| 292 | 4 | 12 | $self->{_type} = "image/$mime_type"; | ||||
| 293 | ### $mime_type | ||||||
| 294 | } | ||||||
| 295 | |||||||
| 296 | sub gen_en_image { | ||||||
| 297 | 7 | 0 | 48 | my ($self, $str) = @_; | |||
| 298 | 7 | 46 | my $angle = 2 + int rand 4; | ||||
| 299 | 7 | 133 | my $captcha = GD::SecurityImage->new( | ||||
| 300 | width => 120, | ||||||
| 301 | height => 30, | ||||||
| 302 | lines => 1, | ||||||
| 303 | font => "$FindBin::Bin/../font/wqy-zenhei.ttf", | ||||||
| 304 | #thickness => 0.5, | ||||||
| 305 | rndmax => 3, | ||||||
| 306 | angle => $angle, | ||||||
| 307 | ptsize => 14, | ||||||
| 308 | #ptsize => 80, | ||||||
| 309 | #send_ctobg => 1, | ||||||
| 310 | #scramble => 1, | ||||||
| 311 | ); | ||||||
| 312 | |||||||
| 313 | #warn $str; | ||||||
| 314 | 7 | 7495 | $captcha->random($str); | ||||
| 315 | 7 | 368 | $captcha->create(ttf => 'default'); | ||||
| 316 | 7 | 5971 | die "Failed to load ttf font for GD: $@\n" if $captcha->gdbox_empty; | ||||
| 317 | 7 | 176 | $captcha->particle(100); # : 1732); | ||||
| 318 | 7 | 91825 | my ($image_data, $mime_type) = $captcha->out(compress => 1); | ||||
| 319 | 7 | 5083 | $self->{_bin_data} = $image_data; | ||||
| 320 | 7 | 27 | $self->{_type} = "image/$mime_type"; | ||||
| 321 | ### $mime_type | ||||||
| 322 | } | ||||||
| 323 | |||||||
| 324 | 1; | ||||||
| 325 | |||||||