with image support

php
Marc Wäckerlin 9 years ago
parent 2d33067f56
commit ceb1ac0958
  1. 194
      html/attachment.svg
  2. 46
      html/chat.html
  3. 4
      html/messagetable.php
  4. 66
      html/safechat.css
  5. 141
      html/safechat.js
  6. 7
      html/send.php

@ -1,160 +1,56 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<!-- Generator: Adobe Illustrator 9.0, SVG Export Plug-In -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://web.resource.org/cc/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48"
height="48"
id="svg79606"
sodipodi:version="0.32"
inkscape:version="0.42+devel"
version="1.0"
sodipodi:docbase="/home/jimmac/src/cvs/tango-icon-theme/scalable/status"
sodipodi:docname="mail-attachment.svg">
<defs
id="defs79608">
<linearGradient
id="linearGradient5783">
<stop
style="stop-color:#d3d7cf;stop-opacity:1;"
offset="0"
id="stop5785" />
<stop
id="stop5791"
offset="0.5"
style="stop-color:#f5f5f5;stop-opacity:1;" />
<stop
style="stop-color:#bebebe;stop-opacity:1;"
offset="1"
id="stop5787" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3558">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3560" />
<stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3562" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3558"
id="radialGradient3564"
cx="21.761711"
cy="23.07144"
fx="21.761711"
fy="23.07144"
r="15.571428"
gradientTransform="matrix(0.977282,3.554943e-8,-8.305337e-10,0.651376,-0.794430,15.82896)"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5783"
id="linearGradient5789"
x1="23.505953"
y1="5.7753429"
x2="20.604948"
y2="29.85923"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
width="48pt"
height="48pt"
viewBox="0 0 48 48"
xml:space="preserve"
id="svg4573"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="attachment.svg"><metadata
id="metadata4589"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs4587" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="0.23529412"
inkscape:pageopacity="0.0"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="35.082015"
inkscape:cy="26.818745"
inkscape:current-layer="layer1"
inkscape:window-width="1920"
inkscape:window-height="1033"
id="namedview4585"
showgrid="false"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="770"
inkscape:window-height="716"
inkscape:window-x="217"
inkscape:window-y="575"
stroke="#d3d7cf"
inkscape:showpageshadow="false" />
<metadata
id="metadata79611">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>Mail Attachment</dc:title>
<dc:date>2005-11-04</dc:date>
<dc:creator>
<cc:Agent>
<dc:title>Andreas Nilsson</dc:title>
</cc:Agent>
</dc:creator>
<dc:source>http://tango-project.org</dc:source>
<dc:subject>
<rdf:Bag>
<rdf:li>attachment</rdf:li>
<rdf:li>file</rdf:li>
</rdf:Bag>
</dc:subject>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
<dc:contributor>
<cc:Agent>
<dc:title>Garrett Lesage</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/2.0/">
<cc:permits
rdf:resource="http://web.resource.org/cc/Reproduction" />
<cc:permits
rdf:resource="http://web.resource.org/cc/Distribution" />
<cc:requires
rdf:resource="http://web.resource.org/cc/Notice" />
<cc:requires
rdf:resource="http://web.resource.org/cc/Attribution" />
<cc:permits
rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
<cc:requires
rdf:resource="http://web.resource.org/cc/ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<path
style="fill:none;stroke:#888a85;stroke-width:3.00000024;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
id="path7057"
d="M 21.326337,9.3278633 L 10.449186,27.94227 C 8.5266861,31.23365 9.6775753,35.481172 13.008091,37.38221 L 15.102397,38.579075 C 18.434077,40.480111 22.732254,39.341738 24.655919,36.05036 L 36.41168,15.928621 C 38.335346,12.636117 37.625044,8.6405654 34.835356,7.0477444 C 32.045435,5.4549233 28.187846,6.8452672 26.265346,10.137772 L 18.109581,24.099704 C 16.186149,27.391081 15.978909,30.871442 17.647547,31.836583 C 19.317351,32.799475 22.257398,30.893938 24.179898,27.602558 L 28.142388,20.81957" />
<path
style="fill:none;stroke:url(#linearGradient5789);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
id="path7053"
d="M 21.326337,9.2155349 L 10.449186,27.829941 C 8.5266861,31.121321 9.6775753,35.368843 13.008091,37.269881 L 15.102397,38.466746 C 18.434077,40.367782 22.732254,39.229409 24.655919,35.938031 L 36.41168,15.816292 C 38.335346,12.523788 37.625044,8.528237 34.835356,6.935416 C 32.045435,5.3425949 28.187846,6.7329388 26.265346,10.025444 L 18.109581,23.987375 C 16.186149,27.278752 15.978909,30.759113 17.647547,31.724254 C 19.317351,32.687146 22.257398,30.781609 24.179898,27.490229 L 28.142388,20.707241" />
<path
sodipodi:type="arc"
style="opacity:0.3125;color:#000000;fill:url(#radialGradient3564);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
id="path3556"
sodipodi:cx="22.571428"
sodipodi:cy="30.857143"
sodipodi:rx="15.571428"
sodipodi:ry="10.142858"
d="M 38.142857 30.857143 A 15.571428 10.142858 0 1 1 7,30.857143 A 15.571428 10.142858 0 1 1 38.142857 30.857143 z"
transform="matrix(1.316514,0.000000,0.000000,0.246479,-2.215601,32.89436)" />
</g>
</svg>
inkscape:zoom="14.3"
inkscape:cx="30"
inkscape:cy="30"
inkscape:window-x="-2"
inkscape:window-y="24"
inkscape:window-maximized="1"
inkscape:current-layer="svg4573" /><g
id="Layer_x0020_3"
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;"><g
id="g4576"><path
style="fill:none;stroke:#FFFFFF;stroke-width:7.504;stroke-linejoin:round;"
d="M18.3,5.4c-0.7,0-1.3,0.6-1.3,1.3v18.9c0,0.4,0.2,0.8,0.5,1l6.1,4.6c0.5,0.4,1.1,0.3,1.6-0.1l5.1-4.4c0.3-0.2,0.4-0.6,0.4-0.9V15c0-0.7-0.6-1.3-1.3-1.3c-0.7,0-1.3,0.6-1.3,1.3c0,0,0,9.1,0,10.1c-0.6,0.5-2.9,2.5-3.9,3.4 c-1.1-0.8-4.1-3.1-4.8-3.6c0-1.1,0-15,0-17.1c1.9,0,10.3,0,12.2,0c0,2.2,0,24.5,0,25.6c-0.8,0.6-6.4,5.1-7.6,6.1c-1.2-0.9-7.1-5.1-7.9-5.7c0-1.2,0-18.5,0-18.5c0-0.7-0.6-1.3-1.3-1.3c-0.7,0-1.3,0.6-1.3,1.3v19.2c0,0.4,0.2,0.8,0.5,1l9.2,6.7 c0.5,0.3,1.1,0.3,1.5,0l8.8-7.1c0.3-0.2,0.5-0.6,0.5-1V6.6c0-0.7-0.6-1.3-1.3-1.3H18.3z"
id="path4578" /><path
style="fill:none;stroke-width:3.3351;stroke-linecap:round;stroke-linejoin:round;"
d="M14.3,15.3v19.2l9.9,6.7l8.8-7.1V6.6H18.3v18.9l6.1,4.6l5.1-4.5V15"
id="path4580" /></g></g><g
id="crop_x0020_marks"
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;"><path
style="fill:none;stroke:none;"
d="M48,48H0V0h48v48z"
id="path4583" /></g></svg>

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

@ -1,12 +1,40 @@
<form id="chat" autocomplete="off" onsubmit="sendmessage(this.elements['recv'].value, this.elements['msg'].value)">
<input placeholder="receiver" autocomplete="off" type="text" id="recv" oninput="checkpartner(this.value)" />
<input placeholder="message" autocomplete="off" type="text" id="msg"/>
<label class="icon" for="file" style="flex-grow: 0"><img style="width: 2em; height: 2em" src="attachment.svg"/></label>
<input class="icon" autocomplete="off" type="file" id="file" style="display:none"/>
<input type="submit" id="send" disabled/>
</form>
<div id="message">
<form id="chat" autocomplete="off" onsubmit="sendmessage(this.elements['recv'].value, this.elements['msg'].value)">
<input placeholder="receiver" autocomplete="off" type="text" id="recv" oninput="checkpartner(this.value)" />
<input placeholder="message" autocomplete="off" type="text" id="msg"/>
<div class="buttongroup">
<span class="toolbutton">
<label for="photo"><img src="photo.svg"/></label>
<input autocomplete="off" type="file" accept="image/*" id="photo" multiple />
</span>
<!--
<span class="toolbutton">
<label for="file"><img src="video.svg"/></label>
<input autocomplete="off" type="file" accept="video/*" id="video"/>
</span>
<span class="toolbutton">
<label for="file"><img src="audio.svg"/></label>
<input autocomplete="off" type="file" accept="audio/*" id="audio"/>
</span>
<span class="toolbutton">
<label for="file"><img src="attachment.svg"/></label>
<input autocomplete="off" type="file" id="file"/>
</span>
-->
<span class="toolbutton">
<label for="send"><img src="send.svg"/></label>
<input type="submit" id="send" disabled/>
</span>
<span class="toolbutton">
<label for="reset"><img src="abort.svg" /></label>
<input type="reset" id="reset" onclick="clearmessage();" />
</span>
</div>
</form>
<div id="preview"></div>
</div>
<div id="msgs"></div>
<script>
$("#file").change(function(evt){fileupload(evt)});
if (!window.FileReader) $("#file").hide(); // not supported by browser
$("#file,#photo,#audio,#video").change(function(evt){fileupload(evt)});
if (!window.FileReader) $("#file,#photo,#audio,#video").hide(); // not supported by browser
</script>

@ -4,7 +4,9 @@ try {
$db = new mysqli("mysql", "root", $_SERVER["MYSQL_ENV_MYSQL_ROOT_PASSWORD"]);
$db->query("create database if not exists safechat;");
$db->select_db("safechat");
$db->query('create table if not exists message (id int primary key not null auto_increment, time timestamp default current_timestamp, user varchar(50) not null, msg text not null);');
$db->query('create table if not exists message (id int primary key not null auto_increment, time timestamp default current_timestamp, user varchar(50) not null, msg longtext not null);');
$db->query('set global max_allowed_packet=1000000000');
$db->query('set global net_buffer_length=1000000');
} catch (Exception $e) {
echo json_encode(null);
}

@ -3,6 +3,12 @@
padding: 0;
}
@media (min-resolution: 120dpi) {
html {
font-size: 120%;
}
}
p {
padding: .5em 0 1em 0;
}
@ -31,6 +37,23 @@ form input#msg {
flex-grow: 4;
}
.buttongroup {
flex-grow: 0;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
}
.buttongroup .toolbutton {
flex-grow: 1;
text-align: center;
}
.toolbutton label img {
height: 1.5em;
}
.toolbutton input {
display:none;
}
table {
width: 100%;
}
@ -74,13 +97,16 @@ td:last-child {
}
}
#status.error {
background-color: lightred;
background-color: red;
color: black;
}
#status.notice {
background-color: yellow;
color: black;
}
#status.success {
background-color: lightgreen;
color: black;
}
#main {
@ -93,36 +119,58 @@ td:last-child {
#msgs .msg {
border: 1px solid black;
margin: 1ex;
padding: 1ex;
border-radius: 2ex;
-moz-border-radius: 1em;
-webkit-border-radius: 1em;
-moz-border-radius: 2ex;
-webkit-border-radius: 2ex;
display: block;
width: auto;
height: auto;
max-width: 60%;
}
#msgs .me {
float: left;
background-color: lightgray;
}
#msgs .other {
float: right;
background-color: lightyellow;
}
#msgs .msg .header {
border-bottom: 1px solid black;
border: 1px solid black;
border-radius: 2ex 2ex 0 0;
-moz-border-radius: 2ex 2ex 0 0;
-webkit-border-radius: 2ex 2ex 0 0;
/*border-radius: 2ex;
-moz-border-radius: 2ex;
-webkit-border-radius: 2ex;*/
margin-bottom: .25ex;
padding-bottom: .25ex;
position: relative;
height: 1em;
padding: .5ex 1ex .5ex 1ex;
}
#msgs .msg .header {
background-color: lightgreen;
}
#msgs .msg .header .date {
font-size: xx-small;
float: left;
/*position: absolute;
left: 0;*/
padding: 0 1ex 0 0;
}
#msgs .msg .header .sender {
font-size: small;
float: right;
/*position: absolute;
right: 0;*/
padding: 0 0 0 1ex;
}
#msgs .msg .text {
float: left;
padding: .5ex 1ex .5ex 1ex;
}
#msgs .msg img {
display: block;
width: 99%;
}
#preview img {
height: 4em;
}

@ -1,5 +1,6 @@
var password = null;
var username = null;
var password = null; // password, only stored temporary, until reload
var username = null; // username, only used during registration
var filecontent = new Array(); // temporary storage for attachments
function error(data, stay) {
$("#status").fadeOut("slow", function() {
@ -11,7 +12,7 @@ function error(data, stay) {
$("#status").html(data);
console.log("error: "+data);
} else {
$("#status").html('<pre>'+JSON.stringify(data)+'</pre>');
$("#status").html('error');
console.log("error: "+JSON.stringify(data));
}
} else {
@ -151,18 +152,46 @@ function userid() {
return publicKey().keys[0].getUserIds()[0];
}
function clearmessage() {
filecontent = new Array();
$('#preview').empty();
$("#msg").val("");
notice("message cleared");
}
function attachments(files, id) {
if (files) files.forEach(function(file) {
//if (file.content.length<1000000) {
var img = document.createElement('img');
img.src = 'data:'+file.type+';base64,' + file.content;
$(id).append(img);
//}
});
}
function fileupload(evt) {
if (!window.FileReader)
return error("your browser dows not support file upload", true);
var reader = new FileReader();
reader.onload = function(evt) {
if (evt.target.readyState!=2)
return notice("ReadyState="+evt.target.readyState);
if (evt.target.error) return error("error reading file", true);
filecontent = evt.target.result;
success(filecontent);
for (var i=0, f; f=evt.target.files[i]; ++i) {
var file = f;
var reader = new FileReader();
reader.onload = function(evt) {
if (evt.target.error) return error("error reading file", true);
if (evt.target.readyState==0) return notice("waiting for data ...");
if (evt.target.readyState==1) return notice("loading data ...");
var base64 = btoa(evt.target.result);
filecontent.push({type: file.type, content: base64});
if (file.type.match('^image/')) {
var img = document.createElement('img');
img.src = 'data:'+file.type+';base64,' + base64;
$("#preview").append(img);
success('image of type '+file.type+' is ready to be sent');
} else {
success('file of type '+file.type+' is ready to be sent');
}
}
reader.readAsBinaryString(file);
}
reader.readAsText(evt.target.files[0]);
}
function setreceiver(name) {
@ -171,83 +200,101 @@ function setreceiver(name) {
$("#msg").focus();
}
var startmsg = 0;
var startmsg = 0; // number of last downloaded message
function get() {
var beeped = false;
$.post("get.php", {start: startmsg}).done(function(res) {
var msgs = JSON.parse(res);
if (msgs) {
msgs.forEach(function(e) {
if (startmsg<Number(e["id"])) startmsg = Number(e["id"]);
$.post("pubkey.php", {user: e["user"]}).done(function(pk) {
if (startmsg<Number(e.id)) startmsg = Number(e.id);
$.post("pubkey.php", {user: e.user}).done(function(pk) {
var res=JSON.parse(pk);
var key=openpgp.key.readArmored(res);
if (!res||key.err) {
error("key of receiver not found", true);
} else {
var message = openpgp.message.readArmored(e["msg"]);
var privkey = privateKey().keys[0];
if (privkey.decrypt(password))
openpgp.decryptAndVerifyMessage(privkey, key.keys, message)
.then(function(msg) {
$("#msgs") // todo: check msg.signatures[0].valid
.prepend('<div id="id'+(e["id"])+'" class="msg '+
(e["user"]==userid()?"me":"other")+
'"><div class="header">'+
'<span class="date">'+
(new Date(1000*Number(e["time"]))).toLocaleString()+
'</span><span class="sender">'+
'<a href="javascript:void(0)" onclick="setreceiver(this.innerHTML)">'+
e["user"]+
'</a></span></div><div class="text">'+
msg.text+
'</div></div><div class="clear"/>');
$('#id'+(e["id"])).emoticonize();
if (!beeped)
(new Audio("A-Tone-His_Self-1266414414.mp3"))
.play();
beeped = true;
});
// .catch(function(e) {
// error("decryption of message failed", true);
// });
setTimeout(get, 10000);
return error("key of receiver not found", true);
}
var message = openpgp.message.readArmored(e.msg);
var privkey = privateKey().keys[0];
if (privkey.decrypt(password))
openpgp.decryptAndVerifyMessage(privkey, key.keys, message)
.then(function(msg) {
var message = JSON.parse(msg.text);
$("#msgs") // todo: check msg.signatures[0].valid
.prepend('<div id="id'+(e.id)+'" class="msg '+
(e.user==userid()?"me":"other")+
'"><div class="header">'+
'<span class="date">'+
(new Date(1000*Number(e.time))).toLocaleString()+
'</span><span class="sender">'+
'<a href="javascript:void(0)" onclick="setreceiver(this.innerHTML)">'+
e.user+
'</a></span></div>'+
'<div class="text">'+
message.text+
'</div></div><div class="clear"/>');
attachments(message.files, '#id'+e.id+' .text');
$('#id'+e.id).emoticonize();
if (!beeped)
(new Audio("A-Tone-His_Self-1266414414.mp3"))
.play();
beeped = true;
})
.catch(function(e) {
// not for me
});
}).fail(function(e) {
error("get sender's key from server failed", true);
});
});
}
setTimeout(get, 10000);
}).fail(error);
}).fail(function(e) {
error("get messages failed")
});
setTimeout(get, 10000);
}
function sendmessage(recv, txt) {
notice("1/3 preparing message ...");
$("#message").fadeOut("slow");
$.post("pubkey.php", {user: recv}).done(function(pk) {
var res=JSON.parse(pk);
var key=openpgp.key.readArmored(res);
if (!res||key.err) {
$("#message").fadeIn("slow");
error("key of receiver not found", true);
return;
}
var privkey = privateKey().keys[0];
privkey.decrypt(password);
openpgp.signAndEncryptMessage(key.keys.concat(publicKey().keys), privkey, txt)
var message = JSON.stringify({text: txt, files: filecontent});
notice("2/3 encrypting message ...");
openpgp.signAndEncryptMessage(key.keys.concat(publicKey().keys), privkey, message)
.then(function(msg) {
notice("3/3 sending message ...");
$.post("send.php", {user: userid(), msg: msg})
.done(function(res) {
if (JSON.parse(res)) {
$("#msg").val("");
$("#message").fadeIn("slow");
clearmessage();
success("message sent");
} else error("error sending message", true);
} else {
$("#message").fadeIn("slow");
error("error sending message", true);
}
})
.fail(error);
})
.catch(function(e) {
$("#message").fadeIn("slow");
error("encryption of message failed", true);
});
}).fail(function(e) {
$("#message").fadeIn("slow");
error("get receiver's key from server failed", true);
});
$("#message").fadeIn("slow");
}
function setpw(pwd) {

@ -4,7 +4,12 @@ try {
$user = $db->real_escape_string($_REQUEST['user']);
$msg = $db->real_escape_string($_REQUEST['msg']);
$q = $db->query("insert into message (user, msg) values ('$user', '$msg');");
echo json_encode(true);
if ($q) {
echo json_encode(true);
} else {
error_log("Error storing message: ".$db->error);
echo json_encode(false);
}
} catch (Exception $e) {
echo json_encode(false);
}

Loading…
Cancel
Save