aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpzread <netfirewall@gmail.com>2013-06-19 01:41:51 +0800
committerpzread <netfirewall@gmail.com>2013-06-19 01:41:51 +0800
commit7623d6373ee00a8b8fbe92f7adc16296aa73688b (patch)
treeaf4e05fdc780e90b554431ca5382ae149b434c6f
parentef9cd376c41e0992a3cbf7e4a2529d6338d68d7c (diff)
downloadtaiwan-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.less12
-rw-r--r--src/css/mail.less13
-rw-r--r--src/css/style.less5
-rw-r--r--src/css/user_main.less3
-rw-r--r--src/html/home.html1
-rw-r--r--src/html/index.html67
-rw-r--r--src/html/login.html21
-rw-r--r--src/html/mail.html73
-rw-r--r--src/html/register.html15
-rw-r--r--src/html/user_main.html12
-rw-r--r--src/js/com.js88
-rw-r--r--src/js/home.js16
-rw-r--r--src/js/index.js94
-rw-r--r--src/js/mail.js112
-rw-r--r--src/js/user.js199
-rwxr-xr-xsrc/py/asyncdb.py3
-rwxr-xr-xsrc/py/backend_server.py24
-rw-r--r--src/py/imc/async.py6
-rw-r--r--src/py/imc/auth.py8
-rwxr-xr-xsrc/py/imc/proxy.py109
-rw-r--r--src/py/mail.py96
-rw-r--r--src/py/mod.py33
-rwxr-xr-xsrc/py/netio.py81
-rwxr-xr-xsrc/py/tojauth.py28
-rwxr-xr-xsrc/py/user.py76
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">沒有帳戶?&nbsp&nbsp&nbsp<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">&times;</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">&times;</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">有帳戶?&nbsp&nbsp&nbsp<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">&times;</button><strong></strong>&nbsp&nbsp<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