$ git clone https://socialnetwork.ion.nu/socialnetwork-web.git
commit 3360442d618ce8968f987d6506a7c5a2485c63ff
Author: Alicia <...>
Date: Sat Jul 15 20:46:12 2017 +0200
Initial friends list implementation.
diff --git a/Makefile b/Makefile
index b2787f2..1371525 100644
--- a/Makefile
+++ b/Makefile
@@ -21,7 +21,7 @@ toolchain/usr/lib/libsocial.so: toolchain/usr/bin/emcc
./buildlibs.sh '$(GMPVERSION)' '$(NETTLEVERSION)' '$(GNUTLSVERSION)'
libsocial.js: jsglue.c toolchain/usr/lib/libsocial.so
- toolchain/usr/bin/emcc -O3 -s EXPORTED_FUNCTIONS="['_social_init','_peer_handlesocket','_peer_new_unique','_websockproxy_read','_websockproxy_setwrite','_jsglue_addfile']" -s RESERVED_FUNCTION_POINTERS=1 -s NO_EXIT_RUNTIME=1 -s NODEJS_CATCH_EXIT=0 -s EXPORTED_RUNTIME_METHODS="['ccall','cwrap','readFile','writeFile']" -s INVOKE_RUN=0 $(JSCFLAGS) $^ $(JSLIBS) -o $@
+ toolchain/usr/bin/emcc -O3 -s EXPORTED_FUNCTIONS="['_social_init','_peer_handlesocket','_peer_new_unique','_websockproxy_read','_websockproxy_setwrite','_getcirclecount','_newcircle','_circle_getcount','_circle_getname','_circle_setname','_social_addfriend','_circle_getid']" -s RESERVED_FUNCTION_POINTERS=1 -s NO_EXIT_RUNTIME=1 -s NODEJS_CATCH_EXIT=0 -s EXPORTED_RUNTIME_METHODS="['ccall','cwrap','readFile','writeFile']" -s INVOKE_RUN=0 $(JSCFLAGS) $^ $(JSLIBS) -o $@
./squeezeandcomment.sh '$@' 'This code was built from gmp $(GMPVERSION), nettle $(NETTLEVERSION), gnutls $(GNUTLSVERSION) and socialnetwork git revision $(SOCIALNETWORKREVISION) with tweaks from socialnetwork-web git revision $(REVISION) using emscripten (fastcomp) $(EMSCRIPTVERSION)'
# Download all external sources we'll need
diff --git a/jsglue.c b/jsglue.c
index d5ae414..858f6af 100644
--- a/jsglue.c
+++ b/jsglue.c
@@ -16,11 +16,13 @@
*/
#include <stdlib.h>
#include <string.h>
+#include <stdio.h>
#include <stdint.h>
#include <time.h>
#include <sys/socket.h>
#include <libsocial/udpstream_private.h>
#include <libsocial/udpstream.h>
+#include <libsocial/social.h>
#include "jsglue.h"
extern struct udpstream* stream_new(int sock, struct sockaddr_storage* addr, socklen_t addrlen);
@@ -42,3 +44,54 @@ void websockproxy_read(struct sockaddr_storage* addr, socklen_t addrlen, const v
stream->recvpackets[stream->recvpacketcount-1].buflen=payloadsize;
memcpy(stream->recvpackets[stream->recvpacketcount-1].buf, buf, payloadsize);
}
+
+unsigned int getcirclecount(void)
+{
+ return social_self->circlecount;
+}
+
+// Find an empty circle and reset it, empty and nameless I guess, then return its index
+unsigned int newcircle(void)
+{
+ unsigned int i;
+ for(i=0; i<social_self->circlecount; ++i)
+ {
+ if(!social_self->circles[i].count && !social_self->circles[i].name){break;}
+ }
+ if(i==social_self->circlecount)
+ {
+ ++social_self->circlecount;
+ social_self->circles=realloc(social_self->circles, social_self->circlecount*sizeof(struct friendslist));
+ }else{
+ free(social_self->circles[i].privacy.circles);
+ }
+ memset(&social_self->circles[i], 0, sizeof(struct friendslist));
+ return i;
+}
+// TODO: how to configure privacy structs?
+
+unsigned int circle_getcount(unsigned int i)
+{
+ if(i>=social_self->circlecount){return 0;}
+ return social_self->circles[i].count;
+}
+const char* circle_getid(unsigned int i, unsigned int u)
+{
+ if(i>=social_self->circlecount){return 0;}
+ if(u>=social_self->circles[i].count){return 0;}
+ unsigned char* bin=social_self->circles[i].friends[u]->id;
+ static char id[ID_SIZE*2+1];
+ sprintf(id, PEERFMT, PEERARG(social_self->circles[i].friends[u]->id));
+ return id;
+}
+const char* circle_getname(unsigned int i)
+{
+ if(i>=social_self->circlecount){return 0;}
+ return social_self->circles[i].name;
+}
+void circle_setname(unsigned int i, const char* name)
+{
+ if(i>=social_self->circlecount){return;}
+ free(social_self->circles[i].name);
+ social_self->circles[i].name=strdup(name);
+}
diff --git a/libsocialjs.js b/libsocialjs.js
index 557afed..77021c5 100644
--- a/libsocialjs.js
+++ b/libsocialjs.js
@@ -25,6 +25,10 @@ function websockwrite(addr, addrlen, buf, size)
var websockproxy_read;
var peer_new_unique;
+var circle_getname;
+var circle_setname;
+var circle_getid;
+var social_addfriend;
var websockproxy_to=false;
var firstpacket=true;
@@ -59,6 +63,15 @@ function init(privkey)
{
websockproxy_read=Module.cwrap('websockproxy_read', null, ['array', 'number', 'array', 'number']);
peer_new_unique=Module.cwrap('peer_new_unique', 'array', ['number', 'array', 'number']);
+ // Low level access functions
+ getcirclecount=Module.cwrap('getcirclecount', 'number', []);
+ newcircle=Module.cwrap('newcircle', 'number', []);
+ circle_getcount=Module.cwrap('circle_getcount', 'number', ['number']);
+ circle_getname=Module.cwrap('circle_getname', 'string', ['number']);
+ circle_setname=Module.cwrap('circle_setname', null, ['number', 'string']);
+ circle_getid=Module.cwrap('circle_getid', 'string', ['number','number']);
+ social_addfriend=Module.cwrap('social_addfriend', null, ['array', 'number']);
+
_websockproxy_setwrite(Runtime.addFunction(websockwrite));
FS.writeFile('privkey.pem', privkey, {});
Module.ccall('social_init', null, ['string', 'string'], ['privkey.pem', '']);
@@ -66,3 +79,36 @@ function init(privkey)
connection.onmessage=handlenet;
connection.onclose=function(){alert('The connection to the server was lost.');};
}
+function getcircles()
+{
+ var circles=new Array();
+ var len=getcirclecount();
+ for(var i=0; i<len; ++i)
+ {
+ var circle=new Object();
+ circle.name=circle_getname(i);
+ circle.index=i;
+ if(circle_getcount(i)>0 || circle.name!=''){circles.push(circle);}
+ }
+ return circles;
+}
+function circle_getfriends(index)
+{
+ // Call C functions to gather number of friends and their IDs
+ var friends=new Array();
+ var count=circle_getcount(index);
+ for(var i=0; i<count; ++i)
+ {
+ friends.push(circle_getid(index, i));
+ }
+ return friends;
+}
+function hextobin(hex)
+{
+ var bin=new Array();
+ for(var i=0; i<hex.length; i+=2)
+ {
+ bin.push(parseInt(hex.substring(i,i+2), 16));
+ }
+ return new Uint8Array(bin);
+}
diff --git a/squeezeandcomment.sh b/squeezeandcomment.sh
index ae4498f..a820cd7 100755
--- a/squeezeandcomment.sh
+++ b/squeezeandcomment.sh
@@ -4,7 +4,6 @@ cp "$file" "${file}.unsqueezed"
comment="$2"
base="`basename "$file"`"
sed -i -e 's|// .*||; /^$/d' "$file" # Probably safe
-sed -i -e 's/throw new[^};]*;\?/;/g' "$file" # Don't waste space on error messages
tr -d '\r\n' < "$file" > ".tmp.${base}" # Not so safe
mv ".tmp.${base}" "$file"
sed -i -e "1i// ${comment}" "$file" # 100% safe
diff --git a/websocial.html b/websocial.html
index e2f2e9f..fc59b39 100644
--- a/websocial.html
+++ b/websocial.html
@@ -43,9 +43,19 @@
Note: generating a key takes a while, you may need to tell your browser to let the script continue.<br />
</div>
</div>
+ <div class="modal" id="circle_window" style="display:none;">
+ Name:<input type="text" id="circle_name" /><br />
+ Privacy:<select id="circle_privacy">
+ <option>Select circles only</option>
+ <option>Friends</option>
+ <option>Everyone</option>
+ </select><br />
+ <div id="circle_circles"></div><br />
+ <button onclick="circle_save();">OK</button> <button onclick="chdisplay('circle_window',false);">Cancel</button>
+ </div>
<!-- Main interface -->
<div class="menu">
- <a href="#">Feed</a><a href="#">Friends</a><a href="#">Self</a>
+ <a href="#" onclick="return false;">Feed</a><a href="#" onclick="page_friends();return false;">Friends</a><a href="#" onclick="return false;">Self</a>
</div>
<div id="display"></div>
</body>
diff --git a/websocial.js b/websocial.js
index f39cbcf..035fc43 100644
--- a/websocial.js
+++ b/websocial.js
@@ -4,10 +4,18 @@ function chdisplay(old,newview)
if(newview){document.getElementById(newview).style.display='block';}
}
+function dom_clear(element)
+{
+ while(element.childNodes.length>0)
+ {
+ element.removeChild(element.childNodes[0]);
+ }
+}
+
function initgui()
{
var key=document.getElementById('privkey').value;
-// TODO: some basic checks that 'key' at least looks like it might be a key, otherwise set to '' so we get the key window
+// TODO: some basic checks that 'key' at least looks like it might be a key, otherwise set to '' so we still get the key window
chdisplay('login_window',(key=='')?'key_window':false);
// Delay the actual work a tiny bit to make sure the "have patience" message is rendered first
setTimeout(function()
@@ -42,6 +50,110 @@ function initgui()
button.onclick=function(){chdisplay('key_window', false);};
button.style.float='right';
w.appendChild(button);
- }, 0);
+ }, 100);
// TODO: display any updates we may have (actually we probably won't have any yet, update when we get them, callback for 'updates' command?)
}
+
+function page_friends()
+{
+ var display=document.getElementById('display');
+ dom_clear(display);
+ var box=document.createElement('div');
+ box.style.width='intrinsic';
+ box.style.width='-webkit-max-content';
+ box.style.width='-moz-max-content';
+ box.style.width='max-content';
+ box.style.marginLeft='auto';
+ box.style.marginRight='auto';
+ var entry=document.createElement('input');
+ entry.placeholder='SocialNetwork ID';
+ entry.size=64;
+ box.appendChild(entry);
+ var circles=getcircles();
+ var circle=document.createElement('select');
+ // List existing circles
+ for(item of circles)
+ {
+ var option=document.createElement('option');
+ option.value=item.index;
+ option.appendChild(document.createTextNode(item.name));
+ circle.appendChild(option);
+ }
+ var option=document.createElement('option');
+ option.value='new';
+ option.text='New circle';
+ circle.appendChild(option);
+ circle.onchange=function()
+ {
+ if(this.value=='new')
+ {
+ configcircle_option=this.selectedOptions[0];
+ circle_openconfig(newcircle());
+ }
+ }
+ box.appendChild(circle);
+ var button=document.createElement('button');
+ button.appendChild(document.createTextNode('Add friend'));
+ button.onclick=function()
+ {
+ if(circle.value=='new') // Need to create the circle first
+ {
+ configcircle_option=circle.selectedOptions[0]; // option;
+ circle.onchange();
+ return;
+ }
+ var id=hextobin(entry.value);
+ social_addfriend(id, circle.value);
+ page_friends();
+ };
+ box.appendChild(button);
+ display.appendChild(box);
+ // TODO: Make this prettier, links to individual profile pages and get the name property
+ for(item of circles)
+ {
+ display.appendChild(document.createTextNode('Circle '+item.name+':'));
+ display.appendChild(document.createElement('br'));
+ var count=circle_getcount(item.index);
+ for(var i=0; i<count; ++i)
+ {
+ var user=circle_getid(item.index, i);
+ display.appendChild(document.createTextNode(user));
+ display.appendChild(document.createElement('br'));
+ }
+ }
+}
+
+var configcircle_index;
+var configcircle_option=false;
+function circle_openconfig(index)
+{
+ // TODO: Move circle-list population to its own function? likely needed for other privacy settings
+ var circles=document.getElementById('circle_circles');
+ dom_clear(circles);
+ // TODO: Populate with circles from libsocial
+ // TODO: Store index and checkbox elements for circle_save()
+ var name=document.getElementById('circle_name');
+ name.value=circle_getname(index);
+ configcircle_index=index;
+ chdisplay(false,'circle_window');
+}
+
+function circle_save()
+{
+ var name=document.getElementById('circle_name');
+ circle_setname(configcircle_index, name.value);
+ if(configcircle_option)
+ {
+ var select=configcircle_option.parentNode;
+ // Update list of circles
+ configcircle_option.text=name.value;
+ configcircle_option.value=configcircle_index;
+ configcircle_option=false;
+ // Make a new 'new circle' option?
+ var option=document.createElement('option');
+ option.value='new';
+ option.text='New circle';
+ select.appendChild(option);
+ }
+ chdisplay('circle_window',false);
+}