diff options
author | pzread <netfirewall@gmail.com> | 2013-06-19 01:41:51 +0800 |
---|---|---|
committer | pzread <netfirewall@gmail.com> | 2013-06-19 01:41:51 +0800 |
commit | 7623d6373ee00a8b8fbe92f7adc16296aa73688b (patch) | |
tree | af4e05fdc780e90b554431ca5382ae149b434c6f | |
parent | ef9cd376c41e0992a3cbf7e4a2529d6338d68d7c (diff) | |
download | taiwan-online-judge-7623d6373ee00a8b8fbe92f7adc16296aa73688b.tar.gz taiwan-online-judge-7623d6373ee00a8b8fbe92f7adc16296aa73688b.tar.zst taiwan-online-judge-7623d6373ee00a8b8fbe92f7adc16296aa73688b.zip |
Add user,mail UI. Fix several bug
-rw-r--r-- | src/css/index.less | 12 | ||||
-rw-r--r-- | src/css/mail.less | 13 | ||||
-rw-r--r-- | src/css/style.less | 5 | ||||
-rw-r--r-- | src/css/user_main.less | 3 | ||||
-rw-r--r-- | src/html/home.html | 1 | ||||
-rw-r--r-- | src/html/index.html | 67 | ||||
-rw-r--r-- | src/html/login.html | 21 | ||||
-rw-r--r-- | src/html/mail.html | 73 | ||||
-rw-r--r-- | src/html/register.html | 15 | ||||
-rw-r--r-- | src/html/user_main.html | 12 | ||||
-rw-r--r-- | src/js/com.js | 88 | ||||
-rw-r--r-- | src/js/home.js | 16 | ||||
-rw-r--r-- | src/js/index.js | 94 | ||||
-rw-r--r-- | src/js/mail.js | 112 | ||||
-rw-r--r-- | src/js/user.js | 199 | ||||
-rwxr-xr-x | src/py/asyncdb.py | 3 | ||||
-rwxr-xr-x | src/py/backend_server.py | 24 | ||||
-rw-r--r-- | src/py/imc/async.py | 6 | ||||
-rw-r--r-- | src/py/imc/auth.py | 8 | ||||
-rwxr-xr-x | src/py/imc/proxy.py | 109 | ||||
-rw-r--r-- | src/py/mail.py | 96 | ||||
-rw-r--r-- | src/py/mod.py | 33 | ||||
-rwxr-xr-x | src/py/netio.py | 81 | ||||
-rwxr-xr-x | src/py/tojauth.py | 28 | ||||
-rwxr-xr-x | src/py/user.py | 76 |
25 files changed, 950 insertions, 245 deletions
diff --git a/src/css/index.less b/src/css/index.less index dfbfaf8..202a348 100644 --- a/src/css/index.less +++ b/src/css/index.less @@ -36,7 +36,7 @@ body{ div.menu{ width:80px; - padding-left:16px; + padding-left:32px; background-color:@darkgray; text-align:left; } @@ -74,9 +74,11 @@ body{ box-shadow:0px 3px 2px -2px fade(@black,10%); } div.menu{ + overflow-x:hidden; li > a{ height:48px; - padding-left:16px; + padding-left:32px; + padding-right:-32px; font-size:@NormalFontSize; font-weight:bold; line-height:48px; @@ -111,6 +113,12 @@ body{ } } } +#index_alert{ + width:370px; + position:fixed; + bottom:@MediumPad; + right:@MediumPad; +} #index_page{ padding-top:@MediumPad; diff --git a/src/css/mail.less b/src/css/mail.less new file mode 100644 index 0000000..433cf2c --- /dev/null +++ b/src/css/mail.less @@ -0,0 +1,13 @@ +#index_page{ + div.newmail{ + div.content{ + width:100%; + height:256px; + } + } + table.maillist{ + tr.item{ + cursor:pointer; + } + } +} diff --git a/src/css/style.less b/src/css/style.less index 7c0b4f8..b38c941 100644 --- a/src/css/style.less +++ b/src/css/style.less @@ -1 +1,6 @@ @import "index.less"; + +div.medium_modal{ + width:970px; + margin-left:-485px; +} diff --git a/src/css/user_main.less b/src/css/user_main.less new file mode 100644 index 0000000..33518fe --- /dev/null +++ b/src/css/user_main.less @@ -0,0 +1,3 @@ +#index_page{ + +} diff --git a/src/html/home.html b/src/html/home.html new file mode 100644 index 0000000..36070c4 --- /dev/null +++ b/src/html/home.html @@ -0,0 +1 @@ +<h1>家</h1> diff --git a/src/html/index.html b/src/html/index.html index 7bcf19d..f476266 100644 --- a/src/html/index.html +++ b/src/html/index.html @@ -5,14 +5,26 @@ <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link href="/bootstrap-toj/css/bootstrap.min.css" rel="stylesheet"> +<link rel="stylesheet" href="/codemirror-3.13/lib/codemirror.css"> +<link rel="stylesheet" href="/codemirror-3.13/theme/lesser-dark.css"> + <link href="/toj/css/style.css" rel="stylesheet" type="text/css"> <script src="/jquery-2.0.2.min.js"></script> <script src="/bootstrap-toj/js/bootstrap.min.js"></script> +<script src="/codemirror-3.13/lib/codemirror.js"></script> +<script src="/codemirror-3.13/mode/xml/xml.js"></script> +<script src="/codemirror-3.13/mode/javascript/javascript.js"></script> +<script src="/codemirror-3.13/mode/css/css.js"></script> +<script src="/codemirror-3.13/mode/htmlmixed/htmlmixed.js"></script> +<script src="http://code.createjs.com/soundjs-0.4.1.min.js"></script> + <script src="/toj/js/imc.js" type="text/javascript"></script> <script src="/toj/js/com.js" type="text/javascript"></script> <script src="/toj/js/index.js" type="text/javascript"></script> <script src="/toj/js/user.js" type="text/javascript"></script> +<script src="/toj/js/home.js" type="text/javascript"></script> +<script src="/toj/js/mail.js" type="text/javascript"></script> <script type="text/javascript"> @@ -20,22 +32,26 @@ $(document).ready(function(){ var j_win = $(window); com.ready(); - index.ready(); - user.ready(); - com.conn_backend(); - j_win.on('resize',com.exheight); - j_win.on('popstat',function(e){ - if(location.href != com.url_curr){ - com.url_prev = com.url_curr; - com.url_curr = location.href; - com.url_chg(); - } - }); + com.conn_callback.add(function(){ + user.ready(); + index.ready(); + home.ready(); + mail.ready(); + + j_win.on('resize',com.exheight); + $(window).on('popstate',function(e){ + if(location.href != com.url_curr){ + com.url_prev = com.url_curr; + com.url_curr = location.href; + com.url_chg(); + } + }); - com.exheight(); - com.url_chg(); + com.exheight(); + com.url_chg(); + }); }); </script> </head> @@ -44,31 +60,32 @@ $(document).ready(function(){ <div id="index_header" class="navbar navbar-fixed-top navbar-inverse"> <div class="navbar-inner"> <div class="container"> - <p class="offset1 span2 navbar-text">TOJ Server Status</p> - <ul class="nav"> - <li class="active"><a href="#">Home</a></li> - <li><a href="#">Profile</a></li> - </ul> + <p class="title offset1 span2 navbar-text"></p> + <ul class="nav"></ul> </div> <div style="position:absolute; top:0px; right:0px;"> <ul class="nav"> - <li><a href="#">Cuvelia</a></li> - <li><a href="#">Logout</a></li> + <li class="nickname" style="display:none;"><a></a></li> + <li class="logout" style="display:none;"><a href="/toj/logout/">登出</a></li> + <li class="login"><a href="/toj/login/">登入</a></li> + <li class="register"><a href="/toj/register/">註冊</a></li> </ul> </div> </div> </div> <div id="index_menutag" class="active"> - <div class="menu">Status</div> + <div class="menu"></div> </div> <div id="index_menu" exheight=true> <div class="tagblock"></div> <div class="menu"> <ul class="nav nav-list"> - <li><a href="#">Home</a></li> - <li class="active"><a href="#">Status</a></li> - <li><a href="#">Mail</a></li> + <li><a href="/toj/home/">首頁</a></li> + <li class="profile" style="display:none;"><a href="">個人</a></li> + <li class="mail" style="display:none;"><a href="/toj/mail/">信箱</a></li> + <li><a href="#">狀態</a></li> + <li><a href="#">關於</a></li> </ul> </div> </div> @@ -104,7 +121,7 @@ $(document).ready(function(){ </div> </div> +<div id="index_alert"></div> <div id="index_page" class="container"></div> - </body> </html> diff --git a/src/html/login.html b/src/html/login.html index d05e5c5..da40433 100644 --- a/src/html/login.html +++ b/src/html/login.html @@ -1,22 +1,17 @@ <link href="/toj/css/login.css" rel="stylesheet"> <div class="row"> - <div class="modal hide fade"> - <div class="modal-header"> - <button class="btn">Files</button> - </div> - <div class="modal-body"> - </div> - </div> - - <div class="info span7"> + <div class="info span6"> <h2>登入TOJ,開始你的解題</h2> <p class="lead">沒有帳戶?   <a href="/toj/register/">註冊</a></p> </div> - <div class="input span6"> + <div class="input offset1 span3"> <h2>登入</h2> - <input type="text" placeholder="使用者名稱"><br> - <input type="text" placeholder="密碼"><br> - <button class="btn btn-primary login">登入</button> + + <div class="alert alert-error" style="display:none;"></div> + + <input name="username" type="text" placeholder="使用者名稱"><br> + <input name="password" type="password" placeholder="密碼"><br> + <button class="btn btn-primary submit">登入</button> </div> </div> diff --git a/src/html/mail.html b/src/html/mail.html new file mode 100644 index 0000000..41250b7 --- /dev/null +++ b/src/html/mail.html @@ -0,0 +1,73 @@ +<link href="/toj/css/mail.css" rel="stylesheet"> +<div class="modal hide fade medium_modal newmail"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> + <h3>寫新郵件</h3> + </div> + <div class="modal-body container-fluid"> + <div class="row-fluid"> + <div class="span13"> + <div class="input-prepend"> + <span class="add-on">收件人</span> + <input class="to_username" type="text" placeholder="使用者名稱"> + </div> + <div class="input-prepend"> + <span class="add-on">標題</span> + <input class="title" type="text"> + </div> + <div class="content"></div> + </div> + </div> + </div> + <div class="modal-footer"> + <button class="btn btn-primary submit">寄出</button> + <button class="btn cancel">取消</button> + </div> +</div> +<div class="modal hide fade medium_modal readmail"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> + <h3>範例郵件</h3> + </div> + <div class="modal-body container-fluid"> + <div class="row-fluid"> + <div class="span13"> + <div class="input-prepend"> + <span class="add-on">收件人</span> + <input class="uneditable-input from_username" type="text" value="Alice"> + </div> + </div> + </div> + </div> + <div class="modal-footer"> + <button class="btn cancel">刪除</button> + </div> +</div> + +<div class="row"> + <div class="span2 offset1 oper"> + <ul class="nav nav-tabs nav-stacked"> + <li class="newmail"><a href="">寫新郵件</a></li> + <li><a href="">寄件備份</a></li> + </ul> + </div> + <div class="span7 mail"> + <table class="table table-hover maillist"> + <thead> + <tr> + <th class="span2">發信者</th> + <th class="span3">標題</th> + <th class="span2">時間</th> + </tr> + </thead> + <tbody></tbody> + </table> + <div class="pagination"> + <ul> + <li><a href="#">←</a></li> + <li class="disabled"><a href="#">1</a></li> + <li><a href="#">→</a></li> + </ul> + </div> + </div> +</div> diff --git a/src/html/register.html b/src/html/register.html index 312d0d7..43d07bb 100644 --- a/src/html/register.html +++ b/src/html/register.html @@ -5,13 +5,16 @@ <h2>註冊TOJ,開始你的解題</h2> <p class="lead">有帳戶?   <a href="/toj/login/">登入</a></p> </div> - <div class="input offset1 span6"> + <div class="input offset1 span3"> <h2>註冊</h2> - <input type="text" placeholder="使用者名稱"><br> - <input type="password" placeholder="密碼"><br> - <input type="password" placeholder="重復密碼"><br> - <input type="text" placeholder="昵稱"><br> - <input type="text" placeholder="信箱"><br> + + <div class="alert alert-error" style="display:none;"></div> + + <input name="username" type="text" placeholder="使用者名稱"><br> + <input name="password" type="password" placeholder="密碼"><br> + <input name="repeat" type="password" placeholder="重復密碼"><br> + <input name="nickname" type="text" placeholder="暱稱"><br> + <input name="email" type="text" placeholder="信箱"><br> <button class="btn btn-primary submit">註冊</button> </div> </div> diff --git a/src/html/user_main.html b/src/html/user_main.html new file mode 100644 index 0000000..0b11358 --- /dev/null +++ b/src/html/user_main.html @@ -0,0 +1,12 @@ +<link href="/toj/css/user_main.css" rel="stylesheet"> + +<div class="row"> + <div class="offset3 span3"> + <img class="img-polaroid avatar" src="http://i.imgur.com/G3Uq50h.png"></img> + </div> + <div class="span7"> + <h3 class="name"></h3> + <h3>關於我</h3> + <p class="aboutme"></p> + </div> +</div> diff --git a/src/js/com.js b/src/js/com.js index d5f5be1..f436b3f 100644 --- a/src/js/com.js +++ b/src/js/com.js @@ -2,15 +2,17 @@ var WebSocketConnection = function(link,ws){ var that = this; - var reader = new FileReader; that.__super__(link); that.send_msg = function(data){ + console.log(ws.readyState); ws.send(new Blob([data],{'type':'application/octet-stream'})) }; that.start_recv = function(recv_callback){ ws.onmessage = function(e){ + var reader = new FileReader; + reader.onload = function(e){ recv_callback(that,e.target.result); }; @@ -106,8 +108,8 @@ return true; that.url_pbox = null; that.link = null; - that.idendesc = null; that.backend_link = null; + that.conn_callback = $.Callbacks(); that.ready = function(){ var i; @@ -174,8 +176,6 @@ return true; }; that.url_pull_pbox = function(){ that.url_update(that.url_pbox); - //window.history.back(); - // }; that.url_chg = function(){ var i; @@ -409,10 +409,12 @@ return true; urlchg_reen = false; }; - that.loadpage = function(htmlurl,callback){ + that.loadpage = function(menu,htmlurl){ var j_index_page = $('#index_page'); var defer = $.Deferred(); + index.set_menu(menu); + index.set_title(''); j_index_page.empty(); j_index_page.load(htmlurl,function(data,stat,xhr){ defer.resolve(); @@ -436,25 +438,68 @@ return true; extop = extop.match('(.*)px')[1]; j_e.height(winheight - extop); } - } + }; + that.get_cookie = function(){ + var ret; + var i; + var part; + var subpart; + + ret = new Object(); + part = document.cookie.split(';'); + if(part.length == 0){ + return null; + } + for(i = 0;i < part.length;i++){ + part[i] = part[i].replace(' ','').replace(/\+/g,' '); + subpart = part[i].split('='); + ret[decodeURIComponent(subpart[0])] = decodeURIComponent(subpart[1]); + } + + return ret; + }; + that.create_codebox = function(j_div,mode){ + var codebox = CodeMirror(j_div[0],{ + 'mode':mode, + 'theme':'lesser-dark', + 'lineNumbers':true, + 'matchBrackets':true, + 'indentUnit':4 + }); + + codebox.getWrapperElement().style.width = '100%'; + codebox.getWrapperElement().style.height = '100%'; + codebox.getScrollerElement().style.width = '100%'; + codebox.getScrollerElement().style.height = '100%'; + + return codebox; + }; + that.is_callerr = function(result){ + if(result.stat == false || typeof(result.data) == 'string'){ + return true; + } + + return false; + }; that.conn_backend = function(){ $.post('http://toj.tfcis.org:83/conn',{},function(res){ var reto; - var iden; + var idendesc; var ws; if(res[0] != 'E'){ reto = JSON.parse(res) that.link = reto.client_link; - that.idendesc = reto.client_idendesc; that.backend_link = reto.backend_link; + idendesc = reto.client_idendesc; ws = new WebSocket('ws://' + reto.ip + ':' + reto.port + '/conn'); ws.onopen = function(){ var i; var conn; + var cookie; console.log('open'); @@ -470,13 +515,36 @@ return true; }); imc.Proxy.instance.add_conn(conn); - imc.Auth.change_current_iden(that.idendesc) + imc.Auth.change_current_iden(idendesc) + + if((cookie = com.get_cookie()) != null){ + com.call_backend('core/user/','cookie_login',function(result){ + if(com.is_callerr(result)){ + //TODO GE + }else{ + imc.Auth.change_current_iden(result.data.idendesc); + } + + that.conn_callback.fire(); + },parseInt(cookie.uid),cookie.hash); + }else{ + that.conn_callback.fire(); + } }; }else{ setTimeout(conn_backend,5000); } }); } -}; + that.call_backend = function(path,func_name,callback){ + var i; + var params = new Array() + params = [com.backend_link + path,func_name,1000,callback] + for(i = 3;i < arguments.length;i++){ + params.push(arguments[i]); + } + imc.Proxy.instance.call.apply(undefined,params); + } +}; diff --git a/src/js/home.js b/src/js/home.js new file mode 100644 index 0000000..1db88dd --- /dev/null +++ b/src/js/home.js @@ -0,0 +1,16 @@ +var home = new function(){ + var that = this; + + that.ready = function(){ + var home_node = new vus.node('home'); + + home_node.url_chg = function(direct,url_upart,url_dpart,param){ + if(direct == 'in'){ + com.loadpage('首頁','/toj/html/home.html').done(function(){ + index.set_title('Taiwan Online Judge'); + }); + } + }; + com.vus_root.child_set(home_node); + }; +} diff --git a/src/js/index.js b/src/js/index.js index e255c87..ce0db25 100644 --- a/src/js/index.js +++ b/src/js/index.js @@ -2,48 +2,58 @@ var index = new function(){ var that = this; - var active_menutag = function(){ - $('#index_menutag').addClass('active'); + var j_win; + var j_menutag; + var j_menu; + var j_paneltag; + var j_panel; + var j_header; + var j_alertbox; + + function active_menutag(){ + j_menutag.addClass('active'); }; - var inactive_menutag = function(){ - if($(window).scrollTop() > 8 && !$('#index_menu').hasClass('active')){ - $('#index_menutag').removeClass('active'); + function inactive_menutag(){ + if(j_win.scrollTop() > 8 && !j_menu.hasClass('active')){ + j_menutag.removeClass('active'); } }; - var active_paneltag = function(){ - $('#index_paneltag').addClass('active'); + function active_paneltag(){ + j_paneltag.addClass('active'); }; - var inactive_paneltag = function(){ - if($(window).scrollTop() > 8 && !$('#index_panel').hasClass('active')){ - $('#index_paneltag').removeClass('active'); + function inactive_paneltag(){ + if(j_win.scrollTop() > 8 && !j_panel.hasClass('active')){ + j_paneltag.removeClass('active'); } }; - var active_menu = function(){ - $('#index_menu').addClass('active'); + function active_menu(){ + j_menu.addClass('active'); active_menutag(); }; - var inactive_menu = function(){ - $('#index_menu').removeClass('active'); + function inactive_menu(){ + j_menu.removeClass('active'); inactive_menutag(); }; - var active_panel = function(){ - $('#index_panel').addClass('active'); + function active_panel(){ + j_panel.addClass('active'); active_paneltag(); }; - var inactive_panel = function(){ - $('#index_panel').removeClass('active'); + function inactive_panel(){ + j_panel.removeClass('active'); inactive_paneltag(); }; that.ready = function(){ - var j_win = $(window); - var j_menutag = $('#index_menutag'); - var j_menu = $('#index_menu'); - var j_paneltag = $('#index_paneltag'); - var j_panel = $('#index_panel'); - + j_win = $(window); + j_menutag = $('#index_menutag'); + j_menu = $('#index_menu'); + j_paneltag = $('#index_paneltag'); + j_panel = $('#index_panel'); + j_header = $('#index_header'); + j_alertbox = $('#index_alert'); + j_win.on('scroll',function(e){ - if($(window).scrollTop() <= 8){ + if(j_win.scrollTop() <= 8){ active_menutag(); active_paneltag(); }else{ @@ -76,12 +86,44 @@ var index = new function(){ active_menu(); }); j_paneltag.find('div.notice').on('click',function(e){ - if($('#index_panel').hasClass('active')){ + if(j_panel.hasClass('active')){ inactive_panel(); }else{ active_panel(); } }); + user.login_callback.add(function(){ + var j_li; + + j_li = j_menu.find('div.menu li.profile') + j_li.find('a').attr('href','/toj/user:' + user.uid + '/main/'); + j_li.show(); + + j_menu.find('div.menu li.mail').show(); + }); + }; + that.set_title = function(title){ + j_header.find('p.title').text(title); + }; + that.set_menu = function(tag){ + j_menutag.find('div.menu').text(tag); + }; + that.add_alert = function(type,title,content,autofade){ + var j_alert; + + j_alert = $('<div class="alert fade in"><button type="button" class="close" data-dismiss="alert">×</button><strong></strong>  <span></span></div>'); + + j_alert.addClass(type); + j_alert.find('strong').text(title); + j_alert.find('span').text(content); + + if(autofade != false){ + setTimeout(function(){ + j_alert.alert('close'); + },10000); + } + + j_alertbox.prepend(j_alert); }; }; diff --git a/src/js/mail.js b/src/js/mail.js new file mode 100644 index 0000000..867b60b --- /dev/null +++ b/src/js/mail.js @@ -0,0 +1,112 @@ +var mail = new function(){ + var that = this; + var j_index_page; + var j_maillist; + + var mailitem_set = function(j_item,from,title,time,unread){ + j_item.find('td.from').text(from); + j_item.find('td.title').text(title); + j_item.find('td.time').text(time); + + if(unread == true){ + j_item.addClass('warning'); + } + }; + var mailitem_create = function(from,title,time,unread){ + var j_item = $('<tr class="item"><td class="from"></td><td class="title"></td><td class="time"></td></tr>'); + + mailitem_set(j_item,from,title,time,unread); + + return j_item; + }; + + that.ready = function(){ + var mail_node = new vus.node('mail'); + + j_index_page = $('#index_page'); + + mail_node.url_chg = function(direct,url_upart,url_dpart,param){ + if(direct == 'in'){ + com.loadpage('信箱','/toj/html/mail.html').done(function(){ + var j_oper; + var j_newmail; + var newmail_content; + + j_maillist = j_index_page.find('table.maillist > tbody'); + + j_oper = j_index_page.find('div.oper'); + j_oper.find('li.newmail > a').on('click',function(e){ + j_newmail.modal('show'); + return false; + }); + + j_newmail = j_index_page.find('div.newmail'); + newmail_content = com.create_codebox(j_newmail.find('div.content'),'text/html'); + + j_newmail.on('shown',function(e){ + newmail_content.refresh(); + }); + j_newmail.on('hide',function(e){ + j_newmail.find('input').val(''); + newmail_content.setValue(''); + }); + j_newmail.find('button.submit').on('click',function(e){ + var to_username = j_newmail.find('input.to_username').val(); + var title = j_newmail.find('input.title').val(); + var content = newmail_content.getValue(); + + com.call_backend('core/mail/','send_mail',function(result){ + var data = result.data; + var errmsg; + + j_newmail.modal('hide'); + + if(com.is_callerr(result)){ + if(data == 'Etitle_too_short'){ + errmsg = '郵件標題過短'; + }else if(data == 'Etitle_too_long'){ + errmsg = '郵件標題過長'; + }else if(data == 'Econtent_too_short'){ + errmsg = '郵件內容過短'; + }else if(data == 'Econtent_too_long'){ + errmsg = '郵件內容過長'; + }else if(data == 'Eto_username'){ + errmsg = '收件人不存在'; + }else{ + errmsg = '信件寄出時發生錯誤'; + } + + index.add_alert('alert-error','失敗',errmsg,true); + }else{ + index.add_alert('alert-success','成功','信件已寄出',true); + } + },to_username,title,content); + }); + j_newmail.find('button.cancel').on('click',function(e){ + j_newmail.modal('hide'); + }); + + com.call_backend('core/mail/','get_mail_count',function(result){ + if(com.is_callerr(result)){ + //TODO GE + }else{ + + } + }); + com.call_backend('core/mail/','list_mail',function(result){ + console.log(result); + },1); + + var j_item; + var i; + + for(i = 0;i < 20;i++){ + j_item = mailitem_create('alice','範例右鍵標題','2013-6-17 10:24'); + j_maillist.append(j_item); + } + }); + } + }; + com.vus_root.child_set(mail_node); + }; +}; diff --git a/src/js/user.js b/src/js/user.js index d76d855..bb6531c 100644 --- a/src/js/user.js +++ b/src/js/user.js @@ -2,46 +2,207 @@ var user = new function(){ var that = this; var j_index_page; + that.uid = null; + that.username = null; + that.nickname = null; + that.email = null; + that.avatar = null; + + that.login_callback = $.Callbacks(); + that.ready = function(){ + var uid; + var user_node_uid = null; + + var user_node = new vus.node('user'); + var main_node = new vus.node('main'); var login_node = new vus.node('login'); var register_node = new vus.node('register'); + var logout_node = new vus.node('logout'); + + function _login(uid,hash,idendesc){ + var expire; + + expire = new Date(); + expire.setDate(expire.getDate() + 30); + document.cookie = 'uid=' + uid + ';path=/;expires=' + expire.toUTCString(); + document.cookie = 'hash=' + hash + ';path=/;expires=' + expire.toUTCString(); + + imc.Auth.change_current_iden(idendesc); + _set_user_data(uid); + }; + function _logout(){ + document.cookie = 'uid=;path=/;expires=Thu, 01 Jan 1970 00:00:00 GMT'; + document.cookie = 'hash=;path=/;expires=Thu, 01 Jan 1970 00:00:00 GMT'; + document.location.href = '/toj/home/'; + }; + function _get_user_info(uid){ + var defer = $.Deferred(); + + com.call_backend('core/user/','get_user_info',function(result){ + if(com.is_callerr(result)){ + defer.reject(result.data); + } + defer.resolve(result.data); + },uid); + + return defer.promise(); + }; + function _set_user_data(uid){ + _get_user_info(uid).done(function(data){ + that.uid = data.uid; + that.username = data.username; + that.nickname = data.nickname; + that.email = data.email; + that.avatar = data.avatar; + + that.login_callback.fire(); + + }).fail(function(data){ + //TODO GE + }); + } j_index_page = $('#index_page'); + if((uid = imc.Auth.get_current_iden().uid) != undefined){ + _set_user_data(uid); + } + + user_node.url_chg = function(direct,url_upart,url_dpart,param){ + if(direct == 'in'){ + user_node_uid = parseInt(param[0]); + } + }; + com.vus_root.child_set(user_node); + + main_node.url_chg = function(direct,url_upart,url_dpart,param){ + if(direct == 'in'){ + $.when( + _get_user_info(user_node_uid), + com.loadpage('使用者','/toj/html/user_main.html') + ).done(function(data){ + j_index_page.find('h3.name').text(data.nickname + '(' + data.username + ')'); + j_index_page.find('p.aboutme').text(data.aboutme); + }); + } + }; + user_node.child_set(main_node); + login_node.url_chg = function(direct,url_upart,url_dpart,param){ if(direct == 'in'){ - com.loadpage('/toj/html/login.html').done(function(){ - j_index_page.find('button.submit').on('click',function(e){ - console.log('test'); + com.loadpage('登入','/toj/html/login.html').done(function(){ + var j_alert = j_index_page.find('div.alert'); + var j_submit = j_index_page.find('button.submit'); + + j_index_page.find('[name="username"]').focus(); + + j_index_page.find('input').on('keypress',function(e){ + if(e.keyCode == 13){ + j_submit.click(); + } + }); + + j_submit.on('click',function(e){ + var username = j_index_page.find('[name="username"]').val(); + var password = j_index_page.find('[name="password"]').val(); + + com.call_backend('core/user/','login',function(result){ + data = result.data; + + if(result.stat == true && typeof(data) != 'string'){ + _login(data.uid,data.hash,data.idendesc); + com.url_push('/toj/home/'); + }else{ + j_alert.text('登入失敗'); + j_alert.show(); + } + },username,password); }); }); } return 'cont' - } + }; com.vus_root.child_set(login_node); register_node.url_chg = function(direct,url_upart,url_dpart,param){ if(direct == 'in'){ - com.loadpage('/toj/html/register.html').done(function(){ + com.loadpage('註冊','/toj/html/register.html').done(function(){ + var j_alert = j_index_page.find('div.alert'); + + j_index_page.find('[name="username"]').focus(); + j_index_page.find('button.submit').on('click',function(e){ - console.log('test'); - - imc.Proxy.instance.register_filter('test/',function(dpart,func_name){ - console.log(dpart); - console.log(func_name); - }); - imc.Proxy.instance.register_call('test/route/','80s',function(callback,a,b){ - console.log(b); - callback('ret'); - }); - imc.Proxy.instance.call(com.backend_link + 'test/','get_client_list',1000,function(result){ - console.log(result); - },1,2); + var username = j_index_page.find('[name="username"]').val(); + var password = j_index_page.find('[name="password"]').val(); + var repeat = j_index_page.find('[name="repeat"]').val(); + var nickname = j_index_page.find('[name="nickname"]').val(); + var email = j_index_page.find('[name="email"]').val(); + + if(password != repeat){ + j_alert.text('重復密碼不同'); + j_alert.show(); + return; + } + + com.call_backend('core/user/','register',function(result){ + data = result.data; + + if(result.stat == true && typeof(data) != 'string'){ + com.call_backend('core/user/','login',function(result){ + data = result.data; + _login(data.uid,data.hash,data.idendesc); + },username,password); + }else{ + if(data == 'Eusername_too_short'){ + j_alert.text('使用者名稱過短'); + }else if(data == 'Eusername_too_long'){ + j_alert.text('使用者名稱過長'); + }else if(data == 'Epassword_too_short'){ + j_alert.text('密碼過短'); + }else if(data == 'Epassword_too_long'){ + j_alert.text('密碼過長'); + }else if(data == 'Enickname_too_short'){ + j_alert.text('暱稱過短'); + }else if(data == 'Enickname_too_long'){ + j_alert.text('暱稱過長'); + }else if(data == 'Eemail_too_short'){ + j_alert.text('信箱過短'); + }else if(data == 'Eemail_too_long'){ + j_alert.text('信箱過長'); + }else if(data == 'Eusername_exists'){ + j_alert.text('使用者名稱已存在'); + } + + j_alert.show(); + } + },username,password,nickname,email,'',''); }); }); } - } + }; com.vus_root.child_set(register_node); + + logout_node.url_chg = function(direct,url_upart,url_dpart,param){ + if(direct == 'in'){ + _logout(); + } + }; + com.vus_root.child_set(logout_node); }; + + that.login_callback.add(function(){ + var j_index_header = $('#index_header'); + var j_a; + + j_a = j_index_header.find('li.nickname > a'); + j_a.text(that.nickname); + j_a.attr('href','/toj/user:' + that.uid + '/main/'); + + j_index_header.find('li.login').hide(); + j_index_header.find('li.register').hide(); + j_index_header.find('li.nickname').show(); + j_index_header.find('li.logout').show(); + }); }; diff --git a/src/py/asyncdb.py b/src/py/asyncdb.py index 1e2bf7a..f999c06 100755 --- a/src/py/asyncdb.py +++ b/src/py/asyncdb.py @@ -278,8 +278,7 @@ class AsyncDB: err = e if err != None or stat == psycopg2.extensions.POLL_OK: - self._ioloop.update_handler(fd, - tornado.ioloop.IOLoop.ERROR) + self._ioloop.update_handler(fd,tornado.ioloop.IOLoop.ERROR) elif stat == psycopg2.extensions.POLL_READ: self._ioloop.update_handler(fd, diff --git a/src/py/backend_server.py b/src/py/backend_server.py index 5412dee..443233e 100755 --- a/src/py/backend_server.py +++ b/src/py/backend_server.py @@ -19,6 +19,7 @@ from imc.proxy import Proxy,Connection,imc_call,imc_call_async,imc_register_call import netio from netio import SocketStream,SocketConnection,WebSocketConnection from tojauth import TOJAuth +import mod class BackendWorker(tornado.tcpserver.TCPServer): def __init__(self,center_addr,ws_port): @@ -109,11 +110,11 @@ class BackendWorker(tornado.tcpserver.TCPServer): Proxy.instance.add_conn(self.center_conn) Proxy.instance.register_call('test/','get_client_list',self._test_get_client_list) - imc_register_call('test/','test_dst',self._test_dst) - Proxy.instance.register_filter('test/',self._test_filter) + Proxy.instance.register_call('test/','test_dst',self._test_dst) + #Proxy.instance.register_filter('test/',self._test_filter) - #imc_register_call('','test_dsta',self._test_dsta) - #$time.sleep(1) + mod.load('core_user','user',self._idendesc,self._get_link) + mod.load('core_mail','mail',self._idendesc,self._get_link) #if self._link == '/backend/2/': # self._test_call(None) @@ -235,7 +236,7 @@ class BackendWorker(tornado.tcpserver.TCPServer): except KeyError: pass - def _get_link(linkclass): + def _get_link(self,linkclass): if linkclass == 'center': return self.center_conn.link @@ -254,15 +255,18 @@ class BackendWorker(tornado.tcpserver.TCPServer): @imc.async.caller def _test_call(self,param): with TOJAuth.change_current_iden(self._idendesc): - for i in range(0,1024): + st = time.perf_counter() + for i in range(0,2): dst = '/backend/' + str((i % 2) + 2) + '/' if dst == self._link: continue - fileres = Proxy.instance.sendfile(dst,'test.py') - ret = imc_call(dst + 'test/','test_dst',fileres.filekey) + fileres = Proxy.instance.sendfile(dst,'Fedora-18-x86_64-DVD.iso') + ret = imc_call_async(dst + 'test/','test_dst',lambda result: print('ok'),fileres.filekey) + print(fileres.wait()) + print(time.perf_counter() - st) print(self._link) #imc_call_async(dst,'test_dst',lambda result : print(result),'test',113) @@ -293,7 +297,9 @@ class BackendWorker(tornado.tcpserver.TCPServer): def _test_dst(self,filekey): print(filekey) - fileres = Proxy.instance.recvfile(filekey,'data') + self._ioloop.add_timeout(datetime.timedelta(milliseconds = 2000),lambda : Proxy.instance.abortfile(filekey)) + Proxy.instance.abortfile(filekey) + #fileres = Proxy.instance.recvfile(filekey,'data') #print('recv ' + fileres.wait()) return 'ok' diff --git a/src/py/imc/async.py b/src/py/imc/async.py index fdb42c4..a9dd048 100644 --- a/src/py/imc/async.py +++ b/src/py/imc/async.py @@ -24,10 +24,8 @@ def switch_top(): try: result = gr_main.switch(None) - except Exception as err: - traceback.print_stack() - print(err) - return (False,'Einternal') + except Exception: + raise finally: tornado.stack_context._state.contexts = old_contexts diff --git a/src/py/imc/auth.py b/src/py/imc/auth.py index 84197a8..cd174ca 100644 --- a/src/py/imc/auth.py +++ b/src/py/imc/auth.py @@ -14,7 +14,7 @@ class Auth: def __init__(self): global current_idendata - self._cache_hashmap = {} + self._cache_hashmap = set() current_idendata = (None,None) Auth.instance = self @@ -98,12 +98,14 @@ class Auth: def _verify(self,data,sig): h = SHA512.new(data) - if h in self._cache_hashmap: + hs = h.hexdigest() + if hs in self._cache_hashmap: return True if self._verifier.verify(h,sig) == True: - self._cache_hashmap[h] = True + self._cache_hashmap.add(hs) return True + else: return False diff --git a/src/py/imc/proxy.py b/src/py/imc/proxy.py index 6b95674..8f750c0 100755 --- a/src/py/imc/proxy.py +++ b/src/py/imc/proxy.py @@ -97,7 +97,7 @@ class Proxy: Proxy.instance = self self.register_call('imc/','pend_recvfile',self._pend_recvfile) - self.register_call('imc/','reject_sendfile',self._reject_sendfile) + self.register_call('imc/','abort_sendfile',self._abort_sendfile) def add_conn(self,conn): assert conn.link not in self._conn_linkmap @@ -153,6 +153,14 @@ class Proxy: child,name,filt = self._walk_path(path,True) filt.append(func) + def unregister_call(self,path,func_name): + child,name,filt = self._walk_path(path,True) + del name[func_name] + + def unregister_filter(self,path,func): + child,name,filt = self._walk_path(path,True) + filt.remove(func) + def call(self,dst,func_name,timeout,*args): return self._route_call(None,self._link,async.get_retid(),Auth.get_current_idendesc(),dst,func_name,timeout,list(args)) @@ -167,6 +175,11 @@ class Proxy: self._ioloop.add_callback(tornado.stack_context.wrap(_call)) def sendfile(self,dst_link,filepath): + def _abort_cb(): + if self._ret_sendfile(filekey,'Eabort'): + with Auth.change_current_iden(self._idendesc,self._auth): + self.call(dst_link + 'imc/','abort_sendfile',65536,filekey) + filekey = SHA512.new(uuid.uuid1().bytes + ssl.RAND_bytes(64)).hexdigest() filesize = os.stat(filepath).st_size @@ -176,14 +189,15 @@ class Proxy: 'filesize':filesize, 'filepath':filepath, 'fileresult':fileresult, - 'timer':self._ioloop.add_timeout(datetime.timedelta(days = 1),lambda : self._ret_sendfile('Etimeout')) + 'timer':self._ioloop.add_timeout(datetime.timedelta(days = 1),lambda : self._ret_sendfile(filekey,'Etimeout')), + 'abort_callback':tornado.stack_context.wrap(_abort_cb) } with Auth.change_current_iden(self._idendesc,self._auth): stat,ret = self.call(dst_link + 'imc/','pend_recvfile',65536,self._link,filekey,filesize) if stat == False: - raise ConnectionError(ret) + self._ret_sendfile(filekey,'Enoexist') return fileresult @@ -202,32 +216,28 @@ class Proxy: self._ioloop.add_callback(self._ret_sendfile,filekey,err) - try: - info = self._info_filekeymap[filekey] - - except KeyError: - return + info = self._info_filekeymap[filekey] src_link = info['src_link'] filesize = info['filesize'] in_conn = self._request_conn(src_link) - self._add_wait_filekey(in_conn.link,filekey,filesize,_callback) - in_conn.recv_file(filekey,filesize,filepath,_callback) - self._send_msg_sendfile(in_conn,src_link,filekey,filesize) + if filekey in self._info_filekeymap: + info['abort_callback'] = tornado.stack_context.wrap(lambda : _callback('Eabort')) + self._add_wait_filekey(in_conn.link,filekey,filesize,_callback) + + in_conn.recv_file(filekey,filesize,filepath,_callback) + self._send_msg_sendfile(in_conn,src_link,filekey,filesize) return info['fileresult'] - def rejectfile(self,filekey): + def abortfile(self,filekey): try: - info = self._info_filekeymap.pop(filekey) + self._info_filekeymap[filekey]['abort_callback']() - except KeyError: - return - - with Auth.change_current_iden(self._idendesc,self._auth): - self.call(info['src_link'] + 'imc/','reject_sendfile',65536,filekey) + except: + pass def _walk_path(self,path,create = False): parts = path.split('/')[:-1] @@ -254,6 +264,13 @@ class Proxy: break return (child,name,filt) + + def _json_handler(self,o): + if isinstance(o,datetime.datetime): + return o.isoformat(); + + else: + return None def _route_call(self,in_conn,caller_link,caller_retid,idendesc,dst,func_name,timeout,param): def __add_wait_caller(conn_link): @@ -403,24 +420,30 @@ class Proxy: try: info = self._info_filekeymap[filekey] if info['filesize'] != filesize: - self._ioloop.add_callback(self._ret_sendfile,filekey,'Efilesize') + raise ValueError - except KeyError: + except (KeyError,ValueError): + self._send_msg_abortfile(out_conn,filekey,'Enoexist') self._ioloop.add_callback(self._ret_sendfile,filekey,'Enoexist') return + info['abort_callback'] = tornado.stack_context.wrap(lambda : __send_cb('Eabort')) self._add_wait_filekey(out_conn.link,filekey,filesize,__send_cb) out_conn.send_file(filekey,info['filepath'],__send_cb) else: in_conn = self._request_conn(src_link) - self._add_wait_filekey(in_conn.link,filekey,filesize,__bridge_cb) - self._add_wait_filekey(out_conn.link,filekey,filesize,__bridge_cb) + if in_conn == None: + self._send_msg_abortfile(out_conn,filekey,'Enoexist') - send_fn = out_conn.send_filedata(filekey,filesize,__bridge_cb) - in_conn.recv_filedata(filekey,filesize,send_fn) + else: + self._add_wait_filekey(in_conn.link,filekey,filesize,__bridge_cb) + self._add_wait_filekey(out_conn.link,filekey,filesize,__bridge_cb) - self._send_msg_sendfile(in_conn,src_link,filekey,filesize) + send_fn = out_conn.send_filedata(filekey,filesize,__bridge_cb) + in_conn.recv_filedata(filekey,filesize,send_fn) + + self._send_msg_sendfile(in_conn,src_link,filekey,filesize) def _add_wait_filekey(self,conn_link,filekey,filesize,callback): callback = tornado.stack_context.wrap(callback) @@ -433,21 +456,22 @@ class Proxy: wait = self._conn_filekeymap[conn_link].pop(filekey) self._ioloop.remove_timeout(wait['timer']) - def _ret_sendfile(self,filekey,err = None): + def _ret_sendfile(self,filekey,err): try: info = self._info_filekeymap.pop(filekey) except KeyError: - return + return False self._ioloop.remove_timeout(info['timer']) - fileresult = info['fileresult'] if err == None: - fileresult.ret_result('Success') - + info['fileresult'].ret_result('Success') + else: - fileresult.ret_result(err) + info['fileresult'].ret_result(err) + + return True def _request_conn(self,link): try: @@ -498,7 +522,7 @@ class Proxy: 'param':param } - conn.send_msg(bytes(json.dumps(msg),'utf-8')) + conn.send_msg(bytes(json.dumps(msg,default = self._json_handler),'utf-8')) def _recv_msg_call(self,conn,msg): @async.caller @@ -523,8 +547,8 @@ class Proxy: 'caller_retid':caller_retid, 'result':{'stat':stat,'data':data} } - - conn.send_msg(bytes(json.dumps(msg),'utf-8')) + + conn.send_msg(bytes(json.dumps(msg,default = self._json_handler),'utf-8')) def _recv_msg_ret(self,conn,msg): caller_link = msg['caller_link'] @@ -542,7 +566,7 @@ class Proxy: 'filesize':filesize } - conn.send_msg(bytes(json.dumps(msg),'utf-8')) + conn.send_msg(bytes(json.dumps(msg,default = self._json_handler),'utf-8')) def _recv_msg_sendfile(self,conn,msg): @async.caller @@ -562,7 +586,7 @@ class Proxy: 'error':err } - conn.send_msg(bytes(json.dumps(msg),'utf-8')) + conn.send_msg(bytes(json.dumps(msg,default = self._json_handler),'utf-8')) def _recv_msg_abortfile(self,conn,msg): @async.caller @@ -580,16 +604,23 @@ class Proxy: @async.caller def _pend_recvfile(self,src_link,filekey,filesize): + def __abort_cb(): + if self._ret_sendfile(filekey,'Eabort'): + with Auth.change_current_iden(self._idendesc,self._auth): + self.call(src_link + 'imc/','abort_sendfile',65536,filekey) + self._info_filekeymap[filekey] = { 'src_link':src_link, 'filesize':filesize, 'fileresult':FileResult(filekey), - 'timer':self._ioloop.add_timeout(datetime.timedelta(days = 1),lambda : self._ret_sendfile('Etimeout')) + 'timer':self._ioloop.add_timeout(datetime.timedelta(days = 1),lambda : self._ret_sendfile(filekey,'Etimeout')), + 'abort_callback':tornado.stack_context.wrap(__abort_cb) } @async.caller - def _reject_sendfile(self,filekey): - self._ioloop.add_callback(self._ret_sendfile,filekey,'Ereject') + def _abort_sendfile(self,filekey): + if filekey in self._info_filekeymap: + self._ioloop.add_callback(self._ret_sendfile,filekey,'Eabort') def imc_call(dst,func_name,*args): return Proxy.instance.call(dst,func_name,65536,*args) diff --git a/src/py/mail.py b/src/py/mail.py index 1dcc2eb..eb733fd 100644 --- a/src/py/mail.py +++ b/src/py/mail.py @@ -1,6 +1,7 @@ from tojauth import TOJAuth from asyncdb import AsyncDB from user import UserMg +from imc.proxy import Proxy import imc.proxy import config @@ -18,17 +19,28 @@ class Mail: LIST_ITEM_PER_PAGE = 20 - def __init__(self, mod_idendesc, get_link): + def __init__(self, mod_idendesc, get_link_fn): Mail.instance = self Mail.db = AsyncDB(config.CORE_DBNAME, config.CORE_DBUSER, config.CORE_DBPASSWORD) Mail._idendesc = mod_idendesc - self.get_link = get_link + self.get_link = get_link_fn + + Proxy.instance.register_call( + 'core/mail/', 'send_mail', self.send_mail) + Proxy.instance.register_call( + 'core/mail/', 'recv_mail', self.recv_mail) + Proxy.instance.register_call( + 'core/mail/', 'list_mail', self.list_mail) + Proxy.instance.register_call( + 'core/mail/', 'del_mail', self.del_mail) + Proxy.instance.register_call( + 'core/mail/', 'get_mail_count', self.get_mail_count) @imc.async.caller - def send_mail(self, to_uid, title, content): + def send_mail(self, to_username, title, content): if( - type(to_uid) != int or + type(to_username) != str or type(title) != str or type(content) != str ): @@ -43,14 +55,13 @@ class Mail: elif len(content) > self.CONTENT_LEN_MAX: return 'Econtent_too_long' - user_iden = TOJAuth.get_current_iden() - try: - uid = user_iden['uid'] - except KeyError: - return 'Euid_error' + to_uid = UserMg.instance.get_uid_by_username(to_username) + if to_uid == None: + return 'Eto_username' - if not UserMg.instance.does_uid_exist(to_uid): - return 'Eto_uid_not_exist' + uid = UserMg.get_current_uid() + if uid == None: + return 'Euid' with TOJAuth.change_current_iden(self._idendesc): self._add_mail( @@ -83,11 +94,9 @@ class Mail: ): return 'Eparameter' - user_iden = TOJAuth.get_current_iden() - try: - uid = user_iden['uid'] - except KeyError: - return 'Euid_error' + uid = UserMg.get_current_uid() + if uid == None: + return 'Eno_uid' with TOJAuth.change_current_iden(self._idendesc): mail = self._get_mail(mailid) @@ -144,11 +153,9 @@ class Mail: ): return 'Eparameter' - user_iden = TOJAuth.get_current_iden() - try: - uid = user_iden['uid'] - except KeyError: - return 'Euid_error' + uid = UserMg.get_current_uid() + if uid == None: + return 'Eno_uid' with TOJAuth.change_current_iden(self._idendesc): maillist = self._get_maillist( @@ -189,11 +196,9 @@ class Mail: ): return 'Eparameter' - user_iden = TOJAuth.get_current_iden() - try: - uid = user_iden['uid'] - except KeyError: - return 'Euid_error' + uid = UserMg.get_current_uid() + if uid == None: + return 'Eno_uid' with TOJAuth.change_current_iden(self._idendesc): mail = self._get_mail(mailid) @@ -214,3 +219,42 @@ class Mail: sqlarr = (mailid, ) cur.execute(sqlstr, sqlarr) + @imc.async.caller + def get_mail_count(self): + uid = UserMg.get_current_uid() + if uid == None: + return 'Eno_uid' + + with TOJAuth.change_current_iden(self._idendesc): + tot_count = self._get_mail_count(uid) + unread_count = self._get_mail_count(uid, True) + + ret = { + 'tot_count': tot_count, + 'unread_count': unread_count + } + + return ret + + @TOJAuth.check_access(_accessid, TOJAuth.ACCESS_EXECUTE) + def _get_mail_count(self, uid, unread = None): + cur = self.db.cursor() + if unread == None: + sqlstr = ('SELECT COUNT(*) FROM "MAIL" WHERE "uid" = %s;') + sqlarr = (uid, ) + else: + sqlstr = ('SELECT COUNT(*) FROM "MAIL" WHERE "uid" = %s AND ' + '"unread" = %s;') + sqlarr = (uid, unread) + cur.execute(sqlstr, sqlarr) + + for data in cur: + count = data[0] + + return count + +def load(mod_idendesc, get_link_fn): + Mail(mod_idendesc, get_link_fn) + +def unload(): + pass diff --git a/src/py/mod.py b/src/py/mod.py new file mode 100644 index 0000000..c41b839 --- /dev/null +++ b/src/py/mod.py @@ -0,0 +1,33 @@ +import sys +from importlib import import_module + +mod_info = {} + +def load(mod_name, mod_path, *args): + if( + mod_name in mod_info or + mod_path in sys.modules + ): + raise NameError + + instance = import_module(mod_path, "") + instance.load(*args) + mod_info[mod_name] = [instance, args, mod_path] + return instance + +def reload(mod_name): + instance, args, mod_path = mod_info[mod_name] + instance = import_module(mod_path, "") + instance.load(*args) + mod_info[mod_name][0] = instance + return instance + + +def unload(mod_name): + instance, args, mod_path = mod_info[mod_name] + instance.unload() + del sys.modules[mod_path] + del mod_info[mod_name] + +def list_mod(): + print(list(mod_info.keys())) diff --git a/src/py/netio.py b/src/py/netio.py index aa2f9b2..045b94c 100755 --- a/src/py/netio.py +++ b/src/py/netio.py @@ -42,11 +42,18 @@ class SocketStream: self._sock.setsockopt(socket.SOL_SOCKET,socket.SO_KEEPALIVE,1) self._sock.setblocking(False) self._ioloop.add_handler(sock.fileno(),self._handle_event,tornado.ioloop.IOLoop.ERROR) + + def _check_close(f): + def wrap(self,*args): + if self._closed == True: + raise ConnectionError - def connect(self,addr,callback): - if self._closed == True: - raise ConnectionError + return f(self,*args) + + return wrap + @_check_close + def connect(self,addr,callback): try: self._conn_callback = tornado.stack_context.wrap(callback) @@ -59,10 +66,8 @@ class SocketStream: except BlockingIOError: pass + @_check_close def read_bytes(self,size,callback = None,nonbuf = False): - if self._closed == True: - raise ConnectionError - if nonbuf == False: self._read_queue.append([self.DATA_BUF,size,bytearray(),tornado.stack_context.wrap(callback)]) @@ -72,19 +77,15 @@ class SocketStream: self._stat |= tornado.ioloop.IOLoop.READ self._ioloop.update_handler(self._sock.fileno(),self._stat) + @_check_close def write(self,buf,callback = None): - if self._closed == True: - raise ConnectionError - self._write_queue.append([self.DATA_BUF,0,buf,tornado.stack_context.wrap(callback)]) self._stat |= tornado.ioloop.IOLoop.WRITE self._ioloop.update_handler(self._sock.fileno(),self._stat) + @_check_close def sendfile(self,fd,callback = None): - if self._closed == True: - raise ConnectionError - size = os.fstat(fd).st_size self._write_queue.append([self.DATA_FILE,size,fd,tornado.stack_context.wrap(callback)]) @@ -92,10 +93,8 @@ class SocketStream: self._stat |= tornado.ioloop.IOLoop.WRITE self._ioloop.update_handler(self._sock.fileno(),self._stat) + @_check_close def recvfile(self,fd,size,callback = None): - if self._closed == True: - raise ConnectionError - self._read_queue.append([self.DATA_FILE,size,fd,tornado.stack_context.wrap(callback)]) self._stat |= tornado.ioloop.IOLoop.READ @@ -118,7 +117,7 @@ class SocketStream: if self._close_callback != None: self._close_callback(self) - + def _handle_event(self,fd,evt): if evt & tornado.ioloop.IOLoop.ERROR: print(os.strerror(self._sock.getsockopt(socket.SOL_SOCKET,socket.SO_ERROR))) @@ -305,12 +304,20 @@ class SocketConnection(Connection): self.file_addr = file_addr self.add_pend_filestream = add_pend_filestream_fn - def send_msg(self,data): - if self._closed == True: - raise ConnectionError + def _check_close(f): + def wrap(self,*args): + if self._closed == True: + raise ConnectionError + return f(self,*args) + + return wrap + + @_check_close + def send_msg(self,data): self.main_stream.write(struct.pack('l',len(data)) + data) + @_check_close def send_file(self,filekey,filepath,callback): def _conn_cb(): self._add_wait_filekey(filekey,_callback) @@ -335,22 +342,20 @@ class SocketConnection(Connection): callback(err) - if self._closed == True: - raise ConnectionError - fd = os.open(filepath,os.O_RDONLY) filesize = os.fstat(fd).st_size file_stream = SocketStream(socket.socket(socket.AF_INET,socket.SOCK_STREAM,0)) - file_stream.set_close_callback(lambda stream : _callback('Eclose')) + file_stream.set_close_callback(lambda stream : _callback('Eabort')) file_stream.connect(self.file_addr,_conn_cb) + @_check_close def recv_file(self,filekey,filesize,filepath,callback): def _conn_cb(stream): nonlocal file_stream file_stream = stream - file_stream.set_close_callback(lambda stream : _callback('Eclose')) + file_stream.set_close_callback(lambda stream : _callback('Eabort')) self._add_wait_filekey(filekey,_callback) file_stream.recvfile(fd,filesize,_callback) @@ -375,14 +380,12 @@ class SocketConnection(Connection): callback(err) - if self._closed == True: - raise ConnectionError - file_stream = None self.add_pend_filestream(filekey,_conn_cb) fd = os.open(filepath,os.O_WRONLY | os.O_CREAT) + @_check_close def send_filedata(self,filekey,filesize,callback): def _conn_cb(): self._add_wait_filekey(filekey,_callback) @@ -416,38 +419,41 @@ class SocketConnection(Connection): file_stream.write(data,__done_cb) - if self._closed == True: - raise ConnectionError - file_stream = SocketStream(socket.socket(socket.AF_INET,socket.SOCK_STREAM,0)) retid = imc.async.get_retid() - file_stream.set_close_callback(lambda stream : _callback('Eclose')) + file_stream.set_close_callback(lambda stream : _callback('Eabort')) file_stream.connect(self.file_addr,_conn_cb) imc.async.switch_top() return _send_cb + @_check_close def recv_filedata(self,filekey,filesize,send_fn): def _conn_cb(stream): nonlocal file_stream file_stream = stream - file_stream.set_close_callback(lambda stream : _callback('Eclose')) + file_stream.set_close_callback(lambda stream : _callback('Eabort')) self._add_wait_filekey(filekey,_callback) file_stream.read_bytes(filesize,send_fn,nonbuf = True) def _callback(err = None): - file_stream.close() + try: + self._del_wait_filekey(filekey) - if self._closed == True: - raise ConnectionError + except KeyError: + return + + file_stream.set_close_callback(None) + file_stream.close() file_stream = None self.add_pend_filestream(filekey,_conn_cb) + @_check_close def abort_file(self,filekey): try: self._sendfile_filekeymap[filekey]('Eabort') @@ -455,6 +461,7 @@ class SocketConnection(Connection): except KeyError: pass + @_check_close def start_recv(self,recv_callback): def _recv_size(data): size, = struct.unpack('l',data) @@ -471,14 +478,12 @@ class SocketConnection(Connection): if self._closed == True: return - traceback.print_stack() - self._closed = True self.main_stream.close() callbacks = list(self._sendfile_filekeymap.values()) for callback in callbacks: - callback('Eclose') + callback('Eabort') super().close() diff --git a/src/py/tojauth.py b/src/py/tojauth.py index 0f775a6..2e44ad5 100755 --- a/src/py/tojauth.py +++ b/src/py/tojauth.py @@ -10,6 +10,8 @@ class TOJAuth(Auth): ACCESS_SETPER = 0x10 ACCESS_EXECUTE = 0x20 + ACCESS_ALL = -1 + ROLETYPE_USER = 1 ROLETYPE_3RD = 2 ROLETYPE_MOD = 3 @@ -17,6 +19,12 @@ class TOJAuth(Auth): ROLETYPE_GROUP = 5 ROLETYPE_GUEST = 6 + ROLEID_TOJ = 1 + ROLEID_MOD = 2 + ROLEID_GUEST = 99 + + ROLEID_SQUARE_ADMIN_GROUP = 101 + _accessid = 1 def __init__(self, pubkey, privkey = None): @@ -92,6 +100,10 @@ class TOJAuth(Auth): return wrapper + @staticmethod + def check_access_func(accessid, access_mask): + TOJAuth.check_access(accessid, access_mask)(lambda x:x)(0) + def create_access(self, owner_idenid): self.check_access( self._accessid, self.ACCESS_EXECUTE)(lambda x:x)(0) @@ -105,6 +117,18 @@ class TOJAuth(Auth): for data in cur: accessid = data[0] return accessid + + def del_access(self, accessid): + self.check_access(accessid, self.ACCESS_SETPER)(lambda x:x)(0) + + cur = self.db.cursor() + sqlstr = ('DELETE FROM "ACCESS_ROLE" WHERE "accessid" = %s;') + sqlarr = (accessid, ) + cur.execute() + + sqlstr = ('DELETE FROM "ACCESS" WHERE "accessid" = %s;') + sqlarr = (accessid, ) + cur.execute() def set_access_list(self, accessid, roleid, permission): self.check_access(accessid, self.ACCESS_SETPER)(lambda x:x)(0) @@ -124,8 +148,8 @@ class TOJAuth(Auth): self.check_access(accessid, self.ACCESS_SETPER)(lambda x:x)(0) cur = self.db.cursor() - sqlstr = ('DELETE FROM "ACCESS_ROLE" WHERE "accessid"=%s ' - 'AND "roleid"=%s;') + sqlstr = ('DELETE FROM "ACCESS_ROLE" WHERE "accessid" = %s ' + 'AND "roleid" = %s;') sqlarr = (accessid, roleid) cur.execute(sqlstr, sqlarr) diff --git a/src/py/user.py b/src/py/user.py index d22d8b3..3c58c03 100755 --- a/src/py/user.py +++ b/src/py/user.py @@ -3,7 +3,8 @@ from Crypto.Hash import SHA512 from tojauth import TOJAuth from asyncdb import AsyncDB -import imc.proxy +import imc.async +from imc.proxy import Proxy import config class UserMg: @@ -22,12 +23,23 @@ class UserMg: ABOUTME_LEN_MIN = 0 ABOUTME_LEN_MAX = 1000 - def __init__(self, mod_idendesc, get_link): + def __init__(self, mod_idendesc, get_link_fn): UserMg.instance = self UserMg.db = AsyncDB(config.CORE_DBNAME, config.CORE_DBUSER, config.CORE_DBPASSWORD) UserMg._idendesc = mod_idendesc - self.get_link = get_link + self.get_link = get_link_fn + + Proxy.instance.register_call( + 'core/user/', 'register', self.register) + Proxy.instance.register_call( + 'core/user/', 'login', self.login) + Proxy.instance.register_call( + 'core/user/', 'cookie_login', self.cookie_login) + Proxy.instance.register_call( + 'core/user/', 'get_user_info', self.get_user_info) + Proxy.instance.register_call( + 'core/user/', 'set_user_info', self.set_user_info) @imc.async.caller def register(self, username, password, nickname, email, avatar, aboutme): @@ -103,7 +115,7 @@ class UserMg: uid = self.get_uid_by_username(username) if uid == None: - return 'Eno_such_uid' + return 'Elogin_failed' passhash = self._password_hash(password) @@ -118,18 +130,22 @@ class UserMg: idenid = data[0] if idenid == None: - return 'Ewrong_password' + return 'Elogin_faild' with TOJAuth.change_current_iden(self._idendesc): - idendesc = TOJAuth.instance.create_iden( - TOJAuth.get_current_iden()['link'], - idenid, - TOJAuth.ROLETYPE_USER, - {'uid' : uid} - ) + stat,data = Proxy.instance.call(self.get_link('center'), + 'create_iden', + 10000, + TOJAuth.get_current_iden()['link'], + idenid, + TOJAuth.ROLETYPE_USER, + {'uid' : uid}) + + if stat == False: + return 'Einternal' ret = { - 'idendesc' : idendesc, + 'idendesc' : data, 'uid' : uid, 'hash' : self._uid_passhash_hash(uid, passhash) } @@ -157,21 +173,25 @@ class UserMg: real_uphash = self._uid_passhash_hash(uid, data[1]) if idenid == None: - return 'Eno_such_uid' + return 'Elogin_failed' if real_uphash != uphash: - return 'Ewrong_uphash' + return 'Elogin_failed' with TOJAuth.change_current_iden(self._idendesc): - idendesc = TOJAuth.instance.create_iden( - TOJAuth.get_current_iden()['link'], - idenid, - TOJAuth.ROLETYPE_USER, - {'uid' : uid} - ) + stat,data = Proxy.instance.call(self.get_link('center'), + 'create_iden', + 10000, + TOJAuth.get_current_iden()['link'], + idenid, + TOJAuth.ROLETYPE_USER, + {'uid' : uid}) + + if stat == False: + return 'Einternal' ret = { - 'idendesc' : idendesc, + 'idendesc' : data, 'uid' : uid, 'hash' : uphash } @@ -340,3 +360,17 @@ class UserMg: return idenid != None + @staticmethod + def get_current_uid(): + user_iden = TOJAuth.get_current_iden() + try: + uid = user_iden['uid'] + except KeyError: + return None + return uid + +def load(mod_idendesc, get_link_fn): + UserMg(mod_idendesc, get_link_fn) + +def unload(): + pass |