$ git clone https://socialnetwork.ion.nu/socialnetwork.git
commit ea0477409b3efb28f5119f8d9bcc86985932a736
Author: Alicia <...>
Date:   Sat Mar 4 00:53:22 2017 +0100

    Added method to get a user's public key without direct connection to the user.

diff --git a/peer.c b/peer.c
index 43a35bb..4aaeb7e 100644
--- a/peer.c
+++ b/peer.c
@@ -445,7 +445,7 @@ printf("Received command '%s' from peer "PEERFMT"\n", peer->cmdname, PEERARG(pee
   }
 }
 
-void peer_sendcmd(struct peer* peer, const char* cmd, void* data, uint32_t len)
+void peer_sendcmd(struct peer* peer, const char* cmd, const void* data, uint32_t len)
 {
   if(!peer) // Broadcast to all connected peers
   {
diff --git a/peer.h b/peer.h
index e366797..655e9de 100644
--- a/peer.h
+++ b/peer.h
@@ -46,7 +46,7 @@ extern struct peer* peer_get(struct udpstream* stream);
 extern struct peer* peer_new_unique(int sock, struct sockaddr* addr, socklen_t addrlen);
 extern void peer_bootstrap(int sock, const char* peerlist);
 extern void peer_handlesocket(int sock); // Incoming data
-extern void peer_sendcmd(struct peer* peer, const char* cmd, void* data, uint32_t len);
+extern void peer_sendcmd(struct peer* peer, const char* cmd, const void* data, uint32_t len);
 extern void peer_disconnect(struct peer* peer, char cleanly);
 extern void peer_findpeer(const unsigned char id[20]); // Find and ask a peer to connect to us
 extern struct peer* peer_findbyid(const unsigned char id[20]);
diff --git a/social.c b/social.c
index c8972af..2e8bf63 100644
--- a/social.c
+++ b/social.c
@@ -38,7 +38,11 @@ static void updateinfo(struct peer* peer, void* data, unsigned int len)
   // <id, 20><sigsize, 4><signature><seq, 8><type, 1><timestamp, 8><type-specific data>
   if(len<20){return;}
   struct user* user=social_finduser(data);
-  if(!user || !user->pubkey){return;}
+  if(!user || !user->pubkey)
+  {
+    if(user){peer_sendcmd(peer, "getpubkey", data, 20);}
+    return;
+  }
   struct update* update=social_update_parse(user, data+20, len-20);
   if(update){social_update_save(user, update);}
 }
@@ -46,7 +50,6 @@ static void updateinfo(struct peer* peer, void* data, unsigned int len)
 static void user_save(struct user* user)
 {
   if(!user->pubkey){return;}
-  // TODO: Absolute path, something like $HOME/.socialnetwork
   char path[strlen(social_prefix)+strlen("/users/0")+40];
   sprintf(path, "%s/users", social_prefix);
   mkdir(path, 0700);
@@ -63,7 +66,6 @@ static void user_save(struct user* user)
 
 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)
   {
@@ -141,6 +143,14 @@ static void greetpeer(struct peer* peer, void* data, unsigned int len)
       peer_sendcmd(user->peer, "getupdates", arg, len);
     }
   }
+  // Ask peer if they have the pubkeys for any of our keyless users
+  for(i=0; i<social_usercount; ++i)
+  {
+    if(!social_users[i]->pubkey)
+    {
+      peer_sendcmd(peer, "getpubkey", social_users[i]->id, 20);
+    }
+  }
 }
 
 static void sendupdate(struct peer* peer, const unsigned char id[20], struct update* update)
@@ -178,6 +188,39 @@ static void sendupdates(struct peer* peer, void* data, unsigned int len)
   }
 }
 
+static void sendpubkey(struct peer* peer, void* data, unsigned int len)
+{ // Request for offline user's public key (note: only direct connections are asked, since only they would send updates anyway)
+  if(len!=20){return;}
+  struct user* user=social_finduser(data);
+  if(!user || !user->pubkey){return;}
+  // Export key
+  gnutls_datum_t key;
+  gnutls_pubkey_export2(user->pubkey, GNUTLS_X509_FMT_DER, &key);
+  // Send key (no need to send ID, receiver gets it from the key)
+  peer_sendcmd(peer, "pubkey", key.data, key.size);
+  gnutls_free(key.data);
+}
+
+static void receivepubkey(struct peer* peer, void* data, unsigned int len)
+{
+  // Import key and get its ID
+  gnutls_datum_t key={.data=data, .size=len};
+  gnutls_pubkey_t pubkey;
+  gnutls_pubkey_init(&pubkey);
+  gnutls_pubkey_import(pubkey, &key, GNUTLS_X509_FMT_DER);
+  unsigned char keyid[20];
+  size_t size=20;
+  gnutls_pubkey_get_key_id(pubkey, 0, keyid, &size);
+  // Find the matching user, if we know them
+  struct user* user=social_finduser(keyid);
+  if(!user || user->pubkey)
+  { // Abort if we don't know them, or if they already have a key
+    gnutls_pubkey_deinit(pubkey);
+    return;
+  }
+  user->pubkey=pubkey;
+}
+
 void social_init(const char* keypath, const char* pathprefix)
 {
   free(social_prefix);
@@ -197,6 +240,8 @@ void social_init(const char* keypath, const char* pathprefix)
   peer_registercmd("updateinfo", updateinfo);
   peer_registercmd("getpeers", greetpeer);
   peer_registercmd("getupdates", sendupdates);
+  peer_registercmd("getpubkey", sendpubkey);
+  peer_registercmd("pubkey", receivepubkey);
 // TODO: Set up socket and bootstrap here too? or accept an already set up socket to bootstrap?
 }
 
@@ -263,7 +308,8 @@ void social_addfriend(const unsigned char id[20], uint32_t circle)
   if(!friend->peer)
   {
     peer_findpeer(id);
-    // TODO: Request updates from any mutual friends we're connected to in the meantime
+    // Request updates from any mutual friends we're connected to in the meantime
+    peer_sendcmd(0, "getpubkey", id, 20);
   }else{
     if(!friend->pubkey)
     {