$ git clone https://socialnetwork.ion.nu/socialnetwork.git
commit 27f86302e63a6832edefc056fba6ea70fa8e62d9
Author: Alicia <...>
Date:   Wed Jan 18 14:06:39 2017 +0100

    Added privacy settings for updates.

diff --git a/social.c b/social.c
index 70e3e18..2958012 100644
--- a/social.c
+++ b/social.c
@@ -59,6 +59,60 @@ static void user_save(struct user* user)
   close(f);
 }
 
+static void user_load(struct user* user)
+{
+  // TODO: Absolute path, something like $HOME/.socialnetwork
+  // Load user data (only pubkey atm), but spare pubkey if it's already set
+  if(!user->pubkey)
+  {
+    char path[strlen("users/0")+40];
+    sprintf(path, "users/"PEERFMT, PEERARG(user->id));
+    int f=open(path, O_RDONLY);
+    if(f>=0)
+    {
+      uint32_t size;
+      read(f, &size, sizeof(size));
+      unsigned char keydata[size];
+      read(f, keydata, size);
+      close(f);
+      gnutls_datum_t key={.data=keydata, .size=size};
+      gnutls_pubkey_init(&user->pubkey);
+      gnutls_pubkey_import(user->pubkey, &key, GNUTLS_X509_FMT_PEM);
+    }
+  }
+  // Load updates
+  char path[strlen("updates/0")+40];
+  sprintf(path, "updates/"PEERFMT, PEERARG(user->id));
+  int f=open(path, O_RDONLY);
+  if(f<0){return;}
+  uint64_t size;
+  while(read(f, &size, sizeof(size))==sizeof(size))
+  {
+    uint8_t buf[size];
+    read(f, buf, size);
+    social_update_parse(user, buf, size);
+  }
+}
+
+static struct user* user_new(const unsigned char id[20])
+{
+  struct user* user=malloc(sizeof(struct user));
+  memcpy(user->id, id, 20);
+  user->pubkey=0;
+  user->peer=peer_findbyid(id);
+  user->name=0;
+  user->circles=0;
+  user->circlecount=0;
+  user->seq=0;
+  user->updates=0;
+  user->updatecount=0;
+  ++social_usercount;
+  social_users=realloc(social_users, sizeof(void*)*social_usercount);
+  social_users[social_usercount-1]=user;
+  user_load(user);
+  return user;
+}
+
 static void greetpeer(struct peer* peer, void* data, unsigned int len)
 {
   // Figure out if they're one of our friends (TODO: or friends of friends)
@@ -109,70 +163,19 @@ static void sendupdates(struct peer* peer, void* data, unsigned int len)
   // "getupdates" can also be requests for data of friends of friends
   user=social_finduser(data);
   if(!user){return;}
+  struct user* peeruser=social_finduser(peer->id);
+  if(!peeruser){peeruser=user_new(peer->id);}
   unsigned int i;
   for(i=0; i<user->updatecount; ++i)
   {
-    // TODO: Check privacy rules
+    // Check privacy rules
+    if(!social_privacy_check(user, &user->updates[i].privacy, peeruser)){continue;}
     // Also make sure not to send old news (based on seq)
     if(user->updates[i].seq<=seq){continue;}
     sendupdate(peer, user->id, &user->updates[i]);
   }
 }
 
-static void user_load(struct user* user)
-{
-  // TODO: Absolute path, something like $HOME/.socialnetwork
-  // Load user data (only pubkey atm), but spare pubkey if it's already set
-  if(!user->pubkey)
-  {
-    char path[strlen("users/0")+40];
-    sprintf(path, "users/"PEERFMT, PEERARG(user->id));
-    int f=open(path, O_RDONLY);
-    if(f>=0)
-    {
-      uint32_t size;
-      read(f, &size, sizeof(size));
-      unsigned char keydata[size];
-      read(f, keydata, size);
-      close(f);
-      gnutls_datum_t key={.data=keydata, .size=size};
-      gnutls_pubkey_init(&user->pubkey);
-      gnutls_pubkey_import(user->pubkey, &key, GNUTLS_X509_FMT_PEM);
-    }
-  }
-  // Load updates
-  char path[strlen("updates/0")+40];
-  sprintf(path, "updates/"PEERFMT, PEERARG(user->id));
-  int f=open(path, O_RDONLY);
-  if(f<0){return;}
-  uint64_t size;
-  while(read(f, &size, sizeof(size))==sizeof(size))
-  {
-    uint8_t buf[size];
-    read(f, buf, size);
-    social_update_parse(user, buf, size);
-  }
-}
-
-static struct user* user_new(const unsigned char id[20])
-{
-  struct user* user=malloc(sizeof(struct user));
-  memcpy(user->id, id, 20);
-  user->pubkey=0;
-  user->peer=peer_findbyid(id);
-  user->name=0;
-  user->circles=0;
-  user->circlecount=0;
-  user->seq=0;
-  user->updates=0;
-  user->updatecount=0;
-  ++social_usercount;
-  social_users=realloc(social_users, sizeof(void*)*social_usercount);
-  social_users[social_usercount-1]=user;
-  user_load(user);
-  return user;
-}
-
 void social_init(const char* keypath)
 {
   // Load key, friends, circles, etc. our own profile
@@ -204,7 +207,7 @@ void social_findfriends(void) // Call a second or so after init (once we have so
   }
 }
 
-void social_user_addtocircle(struct user* user, uint32_t circle, const unsigned char id[20])
+static struct friendslist* user_getcircle(struct user* user, uint32_t circle)
 {
   if(circle>=user->circlecount)
   {
@@ -214,11 +217,19 @@ void social_user_addtocircle(struct user* user, uint32_t circle, const unsigned
       user->circles[user->circlecount].name=0;
       user->circles[user->circlecount].friends=0;
       user->circles[user->circlecount].count=0;
+      user->circles[user->circlecount].privacy.flags=0;
+      user->circles[user->circlecount].privacy.circles=0;
+      user->circles[user->circlecount].privacy.circlecount=0;
     }
   }
+  return &user->circles[circle];
+}
+
+void social_user_addtocircle(struct user* user, uint32_t circle, const unsigned char id[20])
+{
   struct user* friend=social_finduser(id);
   if(!friend){friend=user_new(id);}
-  struct friendslist* c=&user->circles[circle];
+  struct friendslist* c=user_getcircle(user, circle);
   ++c->count;
   c->friends=realloc(c->friends, sizeof(void*)*c->count);
   c->friends[c->count-1]=friend;
@@ -246,20 +257,19 @@ void social_addfriend(const unsigned char id[20], uint32_t circle)
   }
   social_user_addtocircle(social_self, circle, id);
 // TODO: Send a friend request/notification at some point?
-  struct update* update=social_update_new(social_self);
+  struct update* update=social_update_getfriend(social_self, circle, id);
   ++social_self->seq;
   update->seq=social_self->seq;
   update->type=UPDATE_FRIENDS;
   update->timestamp=time(0);
-  update->friends.circle=circle;
+  privcpy(update->privacy, social_self->circles[circle].privacy);
   update->friends.add=1;
-  memcpy(update->friends.id, id, 20);
   social_update_sign(update);
   social_update_save(social_self, update);
   social_shareupdate(update);
 }
 
-void social_createpost(const char* msg)
+void social_createpost(const char* msg, struct privacy* privacy)
 {
   // TODO: Posts attached to users and/or users' updates
   struct update* post=social_update_new(social_self);
@@ -267,20 +277,21 @@ void social_createpost(const char* msg)
   post->seq=social_self->seq;
   post->type=UPDATE_POST;
   post->timestamp=time(0);
+  privcpy(post->privacy, *privacy);
   post->post.message=strdup(msg);
   social_update_sign(post);
   social_update_save(social_self, post);
   social_shareupdate(post);
 }
 
-void social_updatefield(const char* name, const char* value)
+void social_updatefield(const char* name, const char* value, struct privacy* privacy)
 {
-  struct update* post=social_update_new(social_self);
+  struct update* post=social_update_getfield(social_self, name);
   ++social_self->seq;
   post->seq=social_self->seq;
   post->type=UPDATE_FIELD;
   post->timestamp=time(0);
-  post->field.name=strdup(name);
+  privcpy(post->privacy, *privacy);
   post->field.value=strdup(value);
   social_update_sign(post);
   social_update_save(social_self, post);
@@ -307,7 +318,8 @@ void social_shareupdate(struct update* update)
     unsigned int i2;
     for(i2=0; i2<c->count; ++i2)
     {
-// TODO: Privacy settings for updates
+      // Check privacy setting
+      if(!social_privacy_check(social_self, &update->privacy, c->friends[i2])){continue;}
       if(c->friends[i2]->peer)
       {
         sendupdate(c->friends[i2]->peer, social_self->id, update);
@@ -315,3 +327,29 @@ void social_shareupdate(struct update* update)
     }
   }
 }
+
+char social_privacy_check(struct user* origin, struct privacy* privacy, struct user* user)
+{
+  if(privacy->flags&PRIVACY_ANYONE){return 1;}
+  unsigned int i, i2;
+  if(privacy->flags&PRIVACY_FRIENDS)
+  {
+    for(i=0; i<origin->circlecount; ++i)
+    {
+      for(i2=0; i2<origin->circles[i].count; ++i2)
+      {
+        if(origin->circles[i].friends[i2]==user){return 1;}
+      }
+    }
+  }
+  for(i=0; i<privacy->circlecount; ++i)
+  {
+    if(privacy->circles[i]>=origin->circlecount){continue;}
+    struct friendslist* circle=&origin->circles[privacy->circles[i]];
+    for(i2=0; i2<circle->count; ++i2)
+    {
+      if(circle->friends[i2]==user){return 1;}
+    }
+  }
+  return 0;
+}
diff --git a/social.h b/social.h
index 24dafcb..8fb27fe 100644
--- a/social.h
+++ b/social.h
@@ -17,20 +17,26 @@
 #ifndef SOCIAL_H
 #define SOCIAL_H
 #include <stdint.h>
-/* TODO: Privacy
-enum privacy
+#define PRIVACY_ANYONE  1
+#define PRIVACY_FRIENDS 2
+struct privacy
 {
-  PRIVACY_SELF=0,
-  PRIVACY_FRIENDS,
-  PRIVACY_FRIENDSOFFRIENDS,
-  PRIVACY_ANYONE
+  uint8_t flags;
+  uint32_t* circles; // Circle indexes
+  uint32_t circlecount;
+// TODO: Allow individual users as well?
 };
-*/
-// TODO: Posts need to be restrictable to given circles
+#define privcpy(dst,src) \
+  free((dst).circles); \
+  (dst).flags=(src).flags; \
+  (dst).circlecount=(src).circlecount; \
+  (dst).circles=malloc(sizeof(uint32_t)*(dst).circlecount); \
+  memcpy((dst).circles, (src).circles, sizeof(uint32_t)*(dst).circlecount)
 
 struct friendslist
 {
   char* name; // What to call this circle of friends
+  struct privacy privacy; // Privacy setting to use for additions and removals from this circle
   struct user** friends;
   unsigned int count;
 };
@@ -54,8 +60,9 @@ extern struct user* social_self; // Most things we need to keep track of for our
 extern void social_init(const char* keypath);
 extern void social_user_addtocircle(struct user* user, uint32_t circle, const unsigned char id[20]);
 extern void social_addfriend(const unsigned char id[20], uint32_t circle);
-extern void social_createpost(const char* msg);
-extern void social_updatefield(const char* name, const char* value);
+extern void social_createpost(const char* msg, struct privacy* privacy);
+extern void social_updatefield(const char* name, const char* value, struct privacy* privacy);
 extern struct user* social_finduser(const unsigned char id[20]);
 extern void social_shareupdate(struct update* update);
+extern char social_privacy_check(struct user* origin, struct privacy* privacy, struct user* user);
 #endif
diff --git a/socialtest.c b/socialtest.c
index 1170818..33e49e0 100644
--- a/socialtest.c
+++ b/socialtest.c
@@ -41,6 +41,7 @@ int main(int argc, char** argv)
 
   struct pollfd pfd[]={{.fd=0, .events=POLLIN, .revents=0}, {.fd=sock, .events=POLLIN, .revents=0}};
   char buf[1024];
+  struct privacy privacy={.flags=PRIVACY_FRIENDS, .circles=0, .circlecount=0};
   unsigned int i;
   while(1)
   {
@@ -111,7 +112,7 @@ int main(int argc, char** argv)
           if(r<1){break;}
           buf[len+r]=0;
         }
-        social_createpost(buf);
+        social_createpost(buf, &privacy);
       }
       else if(!strncmp(buf, "update field ", 13))
       {
@@ -120,12 +121,77 @@ int main(int argc, char** argv)
         printf("Enter value: "); fflush(stdout);
         unsigned int len=read(0, buf, 1023);
         buf[len]=0;
-        social_updatefield(name, buf);
+        social_updatefield(name, buf, &privacy);
       }
       else if(!strncmp(buf, "exportpeers ", 12))
       {
         peer_exportpeers(&buf[12]);
       }
+      else if(!strcmp(buf, "lscircles"))
+      {
+        for(i=0; i<social_self->circlecount; ++i)
+        {
+          struct friendslist* circle=&social_self->circles[i];
+          printf("%u: %s (%u friends)\n", i, circle->name?circle->name:"Unnamed circle", circle->count);
+        }
+      }
+      else if(!strcmp(buf, "privacy"))
+      {
+        if(!privacy.flags)
+        {
+          printf("%u circles of friends can see updates with this setting:\n", privacy.circlecount);
+          for(i=0; i<privacy.circlecount; ++i)
+          {
+            if(privacy.circles[i]>=social_self->circlecount){printf("Undefined circle\n"); continue;}
+            struct friendslist* circle=&social_self->circles[privacy.circles[i]];
+            printf("%s (%u friends)\n", circle->name?circle->name:"Unnamed circle", circle->count);
+          }
+        }
+        else if(privacy.flags&PRIVACY_ANYONE)
+        {
+          printf("Anyone can see updates with this setting\n");
+        }
+        else if(privacy.flags&PRIVACY_FRIENDS)
+        {
+          printf("Friends in any circle can see updates with this setting\n");
+        }
+        else{printf("Unknown privacy flag set!\n");}
+      }
+      else if(!strncmp(buf, "privacy flag ", 13))
+      {
+        if(!strcmp(&buf[13], "anyone"))
+        {
+          privacy.flags^=PRIVACY_ANYONE;
+        }
+        else if(!strcmp(&buf[13], "friends"))
+        {
+          privacy.flags^=PRIVACY_FRIENDS;
+        }
+        else{printf("Unknown flag '%s'\n", &buf[13]);}
+      }
+      else if(!strncmp(buf, "privacy circle ", 15))
+      {
+        uint32_t circle=strtoul(&buf[15], 0, 0);
+        char found=0;
+        for(i=0; i<privacy.circlecount; ++i)
+        {
+          if(privacy.circles[i]==circle)
+          {
+            --privacy.circlecount;
+            memmove(&privacy.circles[i], &privacy.circles[i+1], sizeof(uint32_t)*(privacy.circlecount-i));
+            found=1;
+            printf("Removed\n");
+            break;
+          }
+        }
+        if(!found)
+        {
+          ++privacy.circlecount;
+          privacy.circles=realloc(privacy.circles, sizeof(uint32_t)*privacy.circlecount);
+          privacy.circles[privacy.circlecount-1]=circle;
+          printf("Added\n");
+        }
+      }
       else{printf("Unknown command '%s'\n", buf);}
     }
     if(pfd[1].revents) // UDP
diff --git a/update.c b/update.c
index 81f2e1d..f23df64 100644
--- a/update.c
+++ b/update.c
@@ -45,6 +45,11 @@ void social_update_write(struct buffer* buf, struct update* update)
   buffer_write(*buf, &update->seq, sizeof(update->seq));
   buffer_write(*buf, &update->type, sizeof(update->type));
   buffer_write(*buf, &update->timestamp, sizeof(update->timestamp));
+  buffer_write(*buf, &update->privacy.flags, sizeof(update->privacy.flags));
+  buffer_write(*buf, &update->privacy.circlecount, sizeof(update->privacy.circlecount));
+  buffer_write(*buf, update->privacy.circles, update->privacy.circlecount);
+  uint32_t privplaceholder=0;
+  buffer_write(*buf, &privplaceholder, sizeof(privplaceholder));
   switch(update->type)
   {
   case UPDATE_FIELD:
@@ -76,6 +81,9 @@ struct update* social_update_new(struct user* user)
 {
   ++user->updatecount;
   user->updates=realloc(user->updates, sizeof(struct update)*user->updatecount);
+  user->updates[user->updatecount-1].privacy.flags=0;
+  user->updates[user->updatecount-1].privacy.circles=0;
+  user->updates[user->updatecount-1].privacy.circlecount=0;
   return &user->updates[user->updatecount-1];
 }
 
@@ -115,7 +123,7 @@ void social_update_save(struct user* user, struct update* update)
 // TODO: Is it bad to close and reopen a file in rapid succession? if it is maybe we should implement some kind of cache for cases where we're saving many updates fast, like receiving someone else's updates for the first time
 }
 
-static struct update* update_getfield(struct user* user, const char* name)
+struct update* social_update_getfield(struct user* user, const char* name)
 {
   unsigned int i;
   for(i=0; i<user->updatecount; ++i)
@@ -131,7 +139,7 @@ static struct update* update_getfield(struct user* user, const char* name)
   return ret;
 }
 
-static struct update* update_getfriend(struct user* user, uint32_t circle, const unsigned char id[20])
+struct update* social_update_getfriend(struct user* user, uint32_t circle, const unsigned char id[20])
 {
   unsigned int i;
   for(i=0; i<user->updatecount; ++i)
@@ -171,6 +179,16 @@ struct update* social_update_parse(struct user* user, void* data, unsigned int l
   readbin(data, len, &seq, sizeof(seq));
   readbin(data, len, &type, sizeof(type));
   readbin(data, len, &timestamp, sizeof(timestamp));
+  // Privacy settings
+  struct privacy privacy;
+  readbin(data, len, &privacy.flags, sizeof(privacy.flags));
+  readbin(data, len, &privacy.circlecount, sizeof(privacy.circlecount));
+  uint32_t privcircles[privacy.circlecount];
+  privacy.circles=privcircles;
+  readbin(data, len, privacy.circles, privacy.circlecount);
+  // Placeholder, potentially supporting lists of individuals in addition to circles
+  uint32_t privplaceholder;
+  readbin(data, len, &privplaceholder, sizeof(privplaceholder));
   // 2. Check sequence number uniqueness
   // NOTE: If instead of checking uniqueness we just checked if seq was higher than the user's previous seq: When relaying updates to a friend's friend a malicious peer could skip some entries, but pass along a more recent one, to effectively censor the earlier entries even when the author themself sends them at a later time. Hopefully it'll be enough that we probably get updates from multiple sources, so if one skips stuff we'll still get filled in by someone else
   unsigned int i;
@@ -199,7 +217,7 @@ struct update* social_update_parse(struct user* user, void* data, unsigned int l
     readbin(data, len, value, valuelen);
     value[valuelen]=0;
     // Erase/replace any old field with the same name if the sequence number is higher
-    update=update_getfield(user, name);
+    update=social_update_getfield(user, name);
     if(update->seq>seq){free(value); return 0;} // Old version
     free((void*)update->signature);
     free((void*)update->field.value);
@@ -233,7 +251,7 @@ struct update* social_update_parse(struct user* user, void* data, unsigned int l
     {
       social_user_addtocircle(user, circle, id);
     } // TODO: Removal
-    update=update_getfriend(user, circle, id);
+    update=social_update_getfriend(user, circle, id);
     if(update->seq>seq){return 0;} // Old version
     update->friends.add=add;
     }
@@ -247,5 +265,6 @@ struct update* social_update_parse(struct user* user, void* data, unsigned int l
   update->seq=seq;
   update->type=type;
   update->timestamp=timestamp;
+  privcpy(update->privacy, privacy);
   return update;
 }
diff --git a/update.h b/update.h
index bb7a301..bda758e 100644
--- a/update.h
+++ b/update.h
@@ -33,7 +33,7 @@ struct update
   uint64_t seq; // Sequence of this update
   uint8_t type;
   uint64_t timestamp;
-// TODO: Privacy settings
+  struct privacy privacy;
   union
   {
     struct // I guess we let it be possible to have any kind of field
@@ -69,4 +69,6 @@ extern void social_update_write(struct buffer* buf, struct update* update);
 extern struct update* social_update_new(struct user* user);
 extern void social_update_sign(struct update* update);
 extern void social_update_save(struct user* user, struct update* update);
+extern struct update* social_update_getfield(struct user* user, const char* name);
+extern struct update* social_update_getfriend(struct user* user, uint32_t circle, const unsigned char id[20]);
 extern struct update* social_update_parse(struct user* user, void* data, unsigned int len); // Both for receiving updates and loading them from file