Pārlūkot izejas kodu

Improve PPRF messages and client. Add PPCL uris

Billy Barrow 1 gadu atpakaļ
vecāks
revīzija
6df2aa4095

+ 111 - 4
src/lib/Ppcl.vala

@@ -10,7 +10,8 @@ namespace Ppub {
         INVALID_ID,
         INVALID_FORMAT,
         INVALID_SHARED_SIGNATURE,
-        INVALID_CREDENTIALS
+        INVALID_CREDENTIALS,
+        INVALID_URI
     }
     
     public class Collection {
@@ -83,7 +84,7 @@ namespace Ppub {
                 if(entry[0] == "SSK") {
                     shared_signature_key = Base64.decode(entry[1]);
                 }
-                if(entry[0] == "MEM") {
+                else if(entry[0] == "MEM") {
                     members.add(new CollectionMember.from_string(entry[1]));
                 }
                 else if(entry[0] == "DOM") {
@@ -177,6 +178,10 @@ namespace Ppub {
             }
         }
 
+        public CollectionUri get_uri(string? via_domain = null) {
+            return new CollectionUri(id, null, null, via_domain);
+        }
+
         public string authoratative_update(uint8[] authoratative_signing_key) {
             serial++;
             modified = new DateTime.now_local();
@@ -237,7 +242,6 @@ namespace Ppub {
             signed_data.append_string(data);
             var checksum_calculator = new Checksum(ChecksumType.SHA512);
             var arr = signed_data.to_array();
-            print(@"ARR.length $(arr.length), str\n\n$data\n");
             checksum_calculator.update(arr, arr.length);
 
             var checksum = new uint8[ChecksumType.SHA512.get_length()];
@@ -433,7 +437,6 @@ namespace Ppub {
             }
 
             var checksum = new BinaryData.from_byte_array(Collection.get_string_checksum(get_signed_portion()));
-            print(@"\t\t$(get_signed_portion()) == $(Base64.encode(checksum.to_array()))\n");
             var signature_data = Sodium.Asymmetric.Signing.verify(signature, collection_member.public_signing_key);
             if(signature_data == null) {
                 throw new CollectionError.INVALID_COLLECTION_SIGNATURE("Invalid publication signature");
@@ -446,4 +449,108 @@ namespace Ppub {
         }
     }
 
+    public class CollectionServerRecord {
+        public uint8[] collection_id { get; set; }
+        public Uri uri { get; set; }
+
+        public CollectionServerRecord(uint8[] collection_id, Uri uri) {
+            this.collection_id = collection_id;
+            this.uri = uri;
+        }
+
+        public CollectionServerRecord.from_string(string str) throws UriError {
+            var parts = str.split(" ");
+            collection_id = Base64.decode(parts[1]);
+            uri = Uri.parse(parts[2], UriFlags.PARSE_RELAXED);
+        }
+
+        public string to_string() {
+            return @"$(Base64.encode(collection_id)): $uri";
+        }
+
+        public static Enumerable<CollectionServerRecord> resolve_records(string name) throws Error {
+            var records = new Vector<CollectionServerRecord>();
+            var resolver = Resolver.get_default();
+            var results = resolver.lookup_records(name, ResolverRecordType.TXT);
+            foreach (var result in results) {
+                foreach (var child in result) {
+                    foreach (var line in child.get_strv()) {
+                        if(line.has_prefix("PPCLSR ")) {
+                            records.add(new CollectionServerRecord.from_string(line));
+                        }
+                    }
+                }
+            }
+
+            return records;
+        }
+    }
+
+    public class CollectionUri {
+
+        public uint8[] collection_id { get; set; }
+        public string? publication { get; set; }
+        public string? asset { get; set; }
+        public string? via_server_record { get; set; }
+
+        public CollectionUri(uint8[] collection_id, string? publication = null, string? asset = null, string? server_record = null) {
+            this.collection_id = collection_id;
+            this.via_server_record = server_record;
+            this.asset = asset;
+            this.publication = publication;
+        }
+
+        public string to_string() {
+            var urlencoded = Base64.encode(collection_id);
+            urlencoded = urlencoded[0:urlencoded.length-2];
+            urlencoded = urlencoded.replace("/", "-");
+            var url = @"ppcl://$urlencoded";
+            if(via_server_record != null) {
+                url += @"@$via_server_record";
+            }
+            if(publication != null) {
+                url += @"/$publication";
+            }
+            if(asset != null) {
+                url += @"/$asset";
+            }
+            return url;
+        }
+
+        private void fill_from_uri(Uri uri) throws CollectionError {
+            if(uri.get_scheme() != "ppcl") {
+                throw new CollectionError.INVALID_URI("Scheme is not ppcl");
+            }
+            var col_id = "";
+            if(uri.get_user() != null) {
+                col_id = uri.get_user();
+                via_server_record = uri.get_host();
+            }
+            else {
+                col_id = uri.get_host();
+            }
+            col_id = col_id.replace("-", "/");
+            col_id += "=";
+            collection_id = Base64.decode(col_id);
+
+            var path = uri.get_path();
+            var parts = path.split("/", 2);
+            if(parts[0] != "") {
+                publication = parts[0];
+            }
+            if(parts.length > 1 && parts[1] != "") {
+                asset = parts[1];
+            }
+        }
+
+        public CollectionUri.from_uri(Uri uri) throws CollectionError {
+            fill_from_uri(uri);
+        }
+
+        public CollectionUri.from_string(string str) throws CollectionError, UriError {
+            fill_from_uri(Uri.parse(str, UriFlags.NON_DNS));
+        }
+
+    }
+
 }

+ 120 - 7
src/pprf/Client.vala

@@ -5,13 +5,35 @@ using Invercargill;
 namespace Pprf {
 
     public errordomain ClientError {
-        FAILED_REQUEST,
         UNEXPECTED_RESPONSE,
     }
 
+    public enum UploadStatus {
+        INITIATING_SESSION,
+        SENDING_CHUNKS,
+        UNPUBLISHING,
+        FINALISING_SESSION,
+        COMPLETE
+    }
+
+    public delegate void UploadProgressDelegate(uint64 bytes_sent, uint64 bytes_total, UploadStatus status);
+
     public abstract class Client {
         public abstract Message send_message(Message message) throws Error;
 
+        private void assert_expected_type(Messages.Message message, Type expected) throws ClientError, Messages.PprfFailureError {
+            var type = Type.from_instance(message);
+            
+            if(message is Messages.Failure) {
+                throw message.to_error();
+            }
+            else if(!type.is_a(expected)) {
+                var expected_type = expected.name().replace("PprfMessages", "");
+                var got_type = type.name().replace("PprfMessages", "");
+                throw new ClientError.UNEXPECTED_RESPONSE(@"Expected $expected_type message or Failure message, got $got_type message");
+            }
+        }
+
         public Message send_authenticated_message(AuthenticatedMessage message, MemberIdentity identity) throws Error {
             var collection = get_collection(identity.collection_id);
             identity.refresh(collection);
@@ -24,15 +46,106 @@ namespace Pprf {
             message.collection_id = identifier;
 
             var response = send_message(message);
-            if(response is Messages.Failure) {
-                throw new ClientError.FAILED_REQUEST(response.message);
+            assert_expected_type(response, typeof(Messages.Collection));
+            return ((Messages.Collection)response).collection;
+        }
+
+        public void publish(BinaryData collection_id, Ppub.CollectionPublication publication, MemberIdentity identity) throws Error {
+            var message = new Messages.Publish();
+            message.collection_id = collection_id;
+            message.publication_string = publication.to_string();
+            var response = send_authenticated_message(message, identity);
+            assert_expected_type(response, typeof(Messages.Confirmation));
+        }
+
+        public void unpublish(BinaryData collection_id, string publication_name, MemberIdentity identity) throws Error {
+            var message = new Messages.Unpublish();
+            message.collection_id = collection_id;
+            message.publication_name = publication_name;
+            var response = send_authenticated_message(message, identity);
+            assert_expected_type(response, typeof(Messages.Confirmation));
+        }
+
+        public void register_name(BinaryData collection_id, string name, MemberIdentity identity) throws Error {
+            var message = new Messages.RegisterName();
+            message.collection_id = collection_id;
+            message.name = name;
+            var response = send_authenticated_message(message, identity);
+            assert_expected_type(response, typeof(Messages.Confirmation));
+        }
+
+        public Messages.UploadSession start_upload_session(BinaryData collection_id, uint64 size, MemberIdentity identity) throws Error {
+            var message = new Messages.BeginUpload();
+            message.collection_id = collection_id;
+            message.file_size = size;
+            message.member_name = identity.name;
+
+            var response = send_message(message);
+            assert_expected_type(response, typeof(Messages.UploadSession));
+            return (Messages.UploadSession)response;
+        }
+
+        public void send_upload_chunk(BinaryData collection_id, Messages.UploadSession session, uint64 offset, Bytes chunk, MemberIdentity identity) throws Error {
+            var message = new Messages.Upload();
+            message.collection_id = collection_id;
+            message.offset = offset;
+            message.upload_chunk = new Messages.BytesMessageBody(chunk);
+            var checksum = Util.data_checksum(chunk.get_data());
+            message.authenticate(session.session_authentication, checksum, identity.credentials);
+
+            var response = send_message(message);
+            assert_expected_type(response, typeof(Messages.Confirmation));
+        }
+
+        public void finalise_upload_session(BinaryData collection_id, Messages.UploadSession session, uint8 flags, string destination, uint8[] checksum, MemberIdentity identity) throws Error {
+            var message = new Messages.FinaliseUpload();
+            message.collection_id = collection_id;
+            message.flags = flags;
+            message.destination = destination;
+            message.authenticate(session.session_authentication, checksum, identity.credentials);
+
+            var response = send_message(message);
+            assert_expected_type(response, typeof(Messages.Confirmation));
+        }
+
+        const uint32 MAX_CHUNK_SIZE = 524288;
+        public void upload(BinaryData collection_id, InputStream data, uint64 size, string destination, bool unpublish_before_finalise, MemberIdentity identity, UploadProgressDelegate? progress_cb = null, uint8 flags = 0) throws Error {
+            UploadProgressDelegate cb = () => {};
+            if(progress_cb != null) {
+                cb = progress_cb;
             }
-            else if(response is Messages.Collection) {
-                return response.collection;
+
+            cb(0, size, UploadStatus.INITIATING_SESSION);
+            var session = start_upload_session(collection_id, size, identity);
+
+            var checksum = new Checksum(ChecksumType.SHA512);
+            var chunk_size = uint32.min(MAX_CHUNK_SIZE, session.max_chunk_size);
+            uint64 offset = 0;
+            
+            cb(0, size, UploadStatus.SENDING_CHUNKS);
+            while(offset < size) {
+                var to_read = uint32.min(chunk_size, (uint32)(size - offset));
+                var chunk = data.read_bytes(to_read);
+                checksum.update(chunk.get_data(), chunk.length);
+                
+                send_upload_chunk(collection_id, session, offset, chunk, identity);
+                offset += chunk.length;
+                cb(offset, size, UploadStatus.SENDING_CHUNKS);
             }
-            else {
-                throw new ClientError.UNEXPECTED_RESPONSE("Expected Collection or Failure message");
+
+            size_t dig_len = 64;
+            var digest = new uint8[64];
+            checksum.get_digest(digest, ref dig_len);
+            digest.length = (int)dig_len;
+
+            if(unpublish_before_finalise) {
+                cb(offset, size, UploadStatus.UNPUBLISHING);
+                unpublish(collection_id, destination, identity);
             }
+
+            cb(offset, size, UploadStatus.FINALISING_SESSION);
+            finalise_upload_session(collection_id, session, flags, destination, digest, identity);
+            cb(offset, size, UploadStatus.COMPLETE);
         }
     }
 

+ 45 - 1
src/pprf/Messages/AuthenticatedMessage.vala

@@ -1,8 +1,52 @@
+using Invercargill;
 
 namespace Pprf.Messages {
 
-    public interface AuthenticatedMessage : Message {
+    public abstract class AuthenticatedMessage : CollectionMessage {
+        public string member_name { get; set; }
+        public uint8[] authentication { get; set; }
+
         public abstract void authenticate(MemberIdentity identity);
+        public abstract bool verify(Ppub.Collection collection);
+
+        public override void deserialise (GLib.DataInputStream stream) throws Error {
+            base.deserialise (stream);
+
+            var name_len = stream.read_byte();
+            if(name_len > 0) {
+                member_name = new BinaryData.from_bytes(stream.read_bytes (name_len)).to_raw_string();
+            }
+
+            var data_len = stream.read_uint16();
+            if(data_len > 0) {
+                authentication = new uint8[data_len];
+                stream.read(authentication);
+            }
+        }
+    
+        public override uint64 calculate_size() {
+            return base.calculate_size() + 
+                1 + // Name length
+                member_name.data.length +
+                2 + // Authentication length
+                authentication.length;
+        }
+        
+        public override void serialise(DataOutputStream stream) throws Error {
+            base.serialise(stream);
+
+            var data_len = member_name.data.length;
+            stream.put_byte((uint8)data_len);
+            if(data_len > 0) {
+                stream.put_string(member_name);
+            }
+
+            data_len = authentication.length;
+            stream.put_uint16((uint16)data_len);
+            if(data_len > 0) {
+                stream.write(authentication);
+            }
+        }
     }
 
 }

+ 46 - 5
src/pprf/Messages/Failure.vala

@@ -42,10 +42,50 @@ namespace Pprf.Messages {
             }
 
         }
+
+        public PprfFailureError to_error() {
+            switch (code) {
+                case 0:
+                    return new PprfFailureError.UNINTELLIGIBLE(message);
+                case 1:
+                    return new PprfFailureError.UNSUPPORTED_MESSAGE(message);
+                case 2:
+                    return new PprfFailureError.UNKNOWN_COLLECTION(message);
+                case 3:
+                    return new PprfFailureError.MEMBER_NOT_IN_COLLECTION(message);
+                case 4:
+                    return new PprfFailureError.INVALID_UPLOAD_SESSION(message);
+                case 5:
+                    return new PprfFailureError.AUTHENTICATION_FAILED(message);
+                case 6:
+                    return new PprfFailureError.INVALID_UPLOAD_OFFSET(message);
+                case 7:
+                    return new PprfFailureError.INVALID_NAME(message);
+                case 8:
+                    return new PprfFailureError.NAME_EXISTS(message);
+                case 9:
+                    return new PprfFailureError.DESTINATION_NOT_REGISTERED(message);
+                case 10:
+                    return new PprfFailureError.DESTINATION_NOT_EMPTY(message);
+                case 11:
+                    return new PprfFailureError.INVALID_MEMBER_SIGNATURE(message);
+                case 13:
+                    return new PprfFailureError.VCDIFF_ERROR(message);
+                case 14:
+                    return new PprfFailureError.PPCL_ERROR(message);
+                case 15:
+                    return new PprfFailureError.DESTINATION_PUBLISHED(message);
+                case 16:
+                    return new PprfFailureError.PUBLICATION_NOT_FOUND(message);
+                default:
+                    return new PprfFailureError.UNKNOWN_ERROR(@"Unknown error with code $code");
+            }
+        }
     }
 
-    public enum FailureCode {
+    public errordomain PprfFailureError {
 
+        UNKNOWN_ERROR = -1,
         UNINTELLIGIBLE = 0,
         UNSUPPORTED_MESSAGE = 1,
         UNKNOWN_COLLECTION = 2,
@@ -58,9 +98,10 @@ namespace Pprf.Messages {
         DESTINATION_NOT_REGISTERED = 9,
         DESTINATION_NOT_EMPTY = 10,
         INVALID_MEMBER_SIGNATURE = 11,
-        INVALID_BSPATCH_CHECKSUM = 12,
-        BSPATCH_ERROR = 13,
-        MALFORMED_PUBLICATION_STRING = 14,
-        DESTINATION_PUBLISHED = 15
+        // Something here for 12
+        VCDIFF_ERROR = 13,
+        PPCL_ERROR = 14,
+        DESTINATION_PUBLISHED = 15,
+        PUBLICATION_NOT_FOUND = 16,
     }
 }

+ 1 - 14
src/pprf/Messages/FinaliseUpload.vala

@@ -5,14 +5,13 @@ namespace Pprf.Messages {
     public enum FinaliseUploadFlags {
         CANCEL = (1 << 0),
         OVERWRITE_DESTINATION = (1 << 1),
-        BSPATCH = (1 << 2)
+        VCDIFF = (1 << 2)
     }
 
     public class FinaliseUpload : UploadSessionMessage {
     
         public uint8 flags { get; set; }
         public string destination { get; set; }
-        public uint8[] bspatch_old_checksum { get; set; }
     
         public FinaliseUpload() {
             message_type = MessageType.FINALISE_UPLOAD;
@@ -30,17 +29,12 @@ namespace Pprf.Messages {
                 stream.read(name_data);
                 destination = new BinaryData.from_byte_array(name_data).to_raw_string();
             }
-            if((flags & FinaliseUploadFlags.BSPATCH) != 0) {
-                bspatch_old_checksum = new uint8[64];
-                stream.read(bspatch_old_checksum);
-            }
         }
     
         public override uint64 calculate_size() {
             return base.calculate_size() + 
                 1 + // Flags
                 2 + // Destination name length
-                ((flags & FinaliseUploadFlags.BSPATCH) == 0 ? 0 : 64) + // bspatch old file checksum (only if flag set)
                 destination.data.length;
         }
         
@@ -54,13 +48,6 @@ namespace Pprf.Messages {
             if(name_size > 0) {
                 stream.put_string(destination);
             }
-
-            if((flags & FinaliseUploadFlags.BSPATCH) != 0) {
-                if(bspatch_old_checksum.length != 64) {
-                    error("BSPATCH flag set but old checksum property is not correct length");
-                }
-                stream.write(bspatch_old_checksum);
-            }
         }
     }
 }

+ 18 - 18
src/pprf/Messages/Publish.vala

@@ -1,23 +1,37 @@
 using Invercargill;
+using Invercargill.Convert;
 
 namespace Pprf.Messages {
 
-    public class Publish : CollectionMessage, AuthenticatedMessage {
+    public class Publish : AuthenticatedMessage {
     
         public string publication_string { get; set; }
-        public uint8[] authentication { get; set; }
     
         public Publish() {
             message_type = MessageType.PUBLISH;
         }
 
-        public void authenticate(MemberIdentity identity) {
+        public override void authenticate(MemberIdentity identity) {
+            member_name = identity.name;
             var checksum = Util.string_checksum(publication_string);
             var auth = new BinaryData.from_byte_array(checksum);
             auth.append(identity.current_state_token);
 
             authentication = identity.sign_with_individual_signature(auth.to_array());
         }
+
+        public override bool verify(Ppub.Collection collection) {
+            var member = collection.members.first_or_default(m => m.name == member_name);
+            var auth = Sodium.Asymmetric.Signing.verify(authentication, member.public_signing_key);
+            if(auth == null)
+                return false;
+
+            var checksum = Util.string_checksum(publication_string);
+            var compare = new BinaryData.from_byte_array(checksum);
+            compare.append_byte_array(collection.current_state_token);
+            
+            return compare.equals(ate(auth));
+        }
     
         public override void deserialise (GLib.DataInputStream stream) throws Error {
             base.deserialise (stream);
@@ -26,20 +40,12 @@ namespace Pprf.Messages {
             if(data_len > 0) {
                 publication_string = new BinaryData.from_bytes(stream.read_bytes (data_len)).to_raw_string();
             }
-
-            data_len = stream.read_uint16();
-            if(data_len > 0) {
-                authentication = new uint8[data_len];
-                stream.read(authentication);
-            }
         }
     
         public override uint64 calculate_size() {
             return base.calculate_size() + 
                 2 + // Data length
-                publication_string.data.length +
-                2 + // Authentication length
-                authentication.length;
+                publication_string.data.length;
         }
         
         public override void serialise(DataOutputStream stream) throws Error {
@@ -50,12 +56,6 @@ namespace Pprf.Messages {
             if(data_len > 0) {
                 stream.put_string(publication_string);
             }
-
-            data_len = authentication.length;
-            stream.put_uint16((uint16)data_len);
-            if(data_len > 0) {
-                stream.write(authentication);
-            }
         }
     }
 }

+ 25 - 25
src/pprf/Messages/RegisterName.vala

@@ -1,54 +1,54 @@
 using Invercargill;
+using Invercargill.Convert;
 
 namespace Pprf.Messages {
 
-    public class RegisterName : CollectionMessage {
+    public class RegisterName : AuthenticatedMessage {
     
-        public string member_name { get; set; }
-        public uint8[] signed_name { get; set; }
+        public string name { get; set; }
     
         public RegisterName() {
             message_type = MessageType.REGISTER_NAME;
         }
 
-        public void sign_name(string name, Ppub.CollectionMemberCredentials credentials) {
-            signed_name = Sodium.Asymmetric.Signing.sign(name.data, credentials.secret_signing_key);
+        public override void authenticate(MemberIdentity identity) {
+            member_name = identity.name;
+            authentication = Sodium.Asymmetric.Signing.sign(Util.string_checksum(name), identity.credentials.secret_signing_key);
+        }
+
+        public override bool verify(Ppub.Collection collection) {
+            var member = collection.members.first_or_default(m => m.name == member_name);
+            var auth = Sodium.Asymmetric.Signing.verify(authentication, member.public_signing_key);
+            if(auth == null)
+                return false;
+
+            var checksum = Util.string_checksum(name);
+            var compare = new BinaryData.from_byte_array(checksum);          
+            return compare.equals(ate(auth));
         }
     
         public override void deserialise (GLib.DataInputStream stream) throws Error {
             base.deserialise (stream);
 
-            var member_len = stream.read_byte();
-            member_name = "";
-            if(member_len > 0) {
-                member_name = new BinaryData.from_bytes(stream.read_bytes (member_len)).to_raw_string();
-            }
-
-            var name_len = stream.read_uint16();
+            var name_len = stream.read_byte();
+            name = "";
             if(name_len > 0) {
-                signed_name = stream.read_bytes (name_len).get_data().copy();
+                name = new BinaryData.from_bytes(stream.read_bytes (name_len)).to_raw_string();
             }
         }
     
         public override uint64 calculate_size() {
             return base.calculate_size() + 
-                1 + // Member name length field
-                member_name.data.length +
-                2 + // Signed name length field
-                signed_name.length;
+                1 + // Name length field
+                name.data.length;
         }
         
         public override void serialise(DataOutputStream stream) throws Error {
             base.serialise(stream);
             
-            stream.put_byte((uint8)member_name.data.length);
-            if(member_name.data.length > 0) {
-                stream.put_string(member_name);
-            }
-
-            stream.put_uint16((uint8)signed_name);
-            if(signed_name.length > 0) {
-                stream.write(signed_name);
+            stream.put_byte((uint8)name.data.length);
+            if(name.data.length > 0) {
+                stream.put_string(name);
             }
         }
     }

+ 61 - 0
src/pprf/Messages/Unpublish.vala

@@ -0,0 +1,61 @@
+using Invercargill;
+using Invercargill.Convert;
+
+namespace Pprf.Messages {
+
+    public class Unpublish : AuthenticatedMessage {
+    
+        public string publication_name { get; set; }
+    
+        public Unpublish() {
+            message_type = MessageType.UNPUBLISH;
+        }
+
+        public override void authenticate(MemberIdentity identity) {
+            member_name = identity.name;
+            var checksum = Util.string_checksum(publication_name);
+            var auth = new BinaryData.from_byte_array(checksum);
+            auth.append(identity.current_state_token);
+
+            authentication = identity.sign_with_individual_signature(auth.to_array());
+        }
+
+        public override bool verify(Ppub.Collection collection) {
+            var member = collection.members.first_or_default(m => m.name == member_name);
+            var auth = Sodium.Asymmetric.Signing.verify(authentication, member.public_signing_key);
+            if(auth == null)
+                return false;
+
+            var checksum = Util.string_checksum(publication_name);
+            var compare = new BinaryData.from_byte_array(checksum);
+            compare.append_byte_array(collection.current_state_token);
+            
+            return compare.equals(ate(auth));
+        }
+    
+        public override void deserialise (GLib.DataInputStream stream) throws Error {
+            base.deserialise (stream);
+
+            var name_len = stream.read_byte();
+            if(name_len > 0) {
+                publication_name = new BinaryData.from_bytes(stream.read_bytes (name_len)).to_raw_string();
+            }
+        }
+    
+        public override uint64 calculate_size() {
+            return base.calculate_size() + 
+                1 + // Name length
+                publication_name.data.length;
+        }
+        
+        public override void serialise(DataOutputStream stream) throws Error {
+            base.serialise(stream);
+
+            var data_len = publication_name.data.length;
+            stream.put_byte((uint8)data_len);
+            if(data_len > 0) {
+                stream.put_string(publication_name);
+            }
+        }
+    }
+}

+ 1 - 0
src/pprf/meson.build

@@ -26,6 +26,7 @@ sources += files('Messages/BeginUpload.vala')
 sources += files('Messages/Upload.vala')
 sources += files('Messages/FinaliseUpload.vala')
 sources += files('Messages/Publish.vala')
+sources += files('Messages/Unpublish.vala')
 sources += files('Messages/Failure.vala')
 sources += files('Messages/Confirmation.vala')
 sources += files('Messages/UploadSession.vala')

+ 17 - 82
src/tools/pprf/Pprf.vala

@@ -2,6 +2,8 @@
 
 public static int main(string[] args) {
 
+    Ppub.CollectionServerRecord.resolve_records("libpeer.pcthingz.com");
+
     var creds = new Ppub.CollectionMemberCredentials.from_string("PYuKgL7SdQYc2Kf6UGG9pCE58m27qrYnCaM45cnxs64=:JbJ6OoNn2KcGX+Tk5C/hotGZCoHOkTNbadUrlk6aCRs=:tL+557eP7kE6ObAW0b5RjvYyU8Dl3oVTOvYA7LAwSdI9i4qAvtJ1BhzYp/pQYb2kITnybbuqticJozjlyfGzrg==:AJbFO6n/cOuD7kk+wu7DmQ58w6z0G3HsukVmIzxGaUM=");
 
     var collection_id = new Invercargill.BinaryData.from_base64("y8ibw54A93LDBKbgWm1EJ/WlbOkGX60DK+qp2lBHpjk=");
@@ -20,94 +22,27 @@ public static int main(string[] args) {
 
     print(@"Acting as $(member.name)\n");
 
-    //  print("Regisering name\n");
-    //  var rmessage = new Pprf.Messages.RegisterName();
-    //  rmessage.collection_id = new Invercargill.BinaryData.from_base64("ctA1tlLAk1hZgEvHcstCJWM+0OkdT0tYdIUeNmRvC5o=");
-    //  rmessage.sign_name (file_name, creds);
-    //  rmessage.member_name = "billy";
-
-    //  var response = client.send_message(rmessage);
-    //  if(response is Pprf.Messages.Failure) {
-    //      print(@"Failure $(response.code): $(response.message)\n");
-    //      return -1;
-    //  }
-
-    print("Beginning upload\n");
-    var message = new Pprf.Messages.BeginUpload();
-    message.collection_id = collection_id;
-    message.file_size = file_size;
-    message.member_name = member.name;
-
-    var response = client.send_message(message);
-    if(response is Pprf.Messages.Failure) {
-        print(@"Failure $(response.code): $(response.message)\n");
-        return -1;
+    print("Regisering name\n");
+    var exists = false;
+    try {
+        client.register_name(collection_id, file_name, member);
     }
-    
-    var session = (Pprf.Messages.UploadSession)response;
-    print(@"Got session! Max chunk size: $(session.max_chunk_size)\n");
-
-    uint64 offset = 0;
-    var full_checksum = new Checksum (ChecksumType.SHA512);
-    var file_stream = upload_file.read();
-    while(offset < file_size) {
-        var chk = new Checksum(ChecksumType.SHA512);
-        var data = file_stream.read_bytes(session.max_chunk_size);
-        chk.update(data.get_data(), data.length);
-        full_checksum.update(data.get_data(), data.length);
-
-        size_t dig_len = 64;
-        var digest = new uint8[64];
-        chk.get_digest(digest, ref dig_len);
-        digest.length = (int)dig_len;
-
-        var umessage = new Pprf.Messages.Upload();
-        umessage.collection_id = collection_id;
-        umessage.offset = offset;
-        umessage.upload_chunk = new Pprf.Messages.BytesMessageBody(data);
-        umessage.authenticate(session.session_authentication, digest, creds);
-
-        response = client.send_message(umessage);
-        if(response is Pprf.Messages.Failure) {
-            print(@"Failure $(response.code): $(response.message)\n");
-            return -1;
-        }
-        offset += data.length;
-        print(@"Sent $offset bytes\n");
+    catch(Pprf.Messages.PprfFailureError.NAME_EXISTS e) {
+        print("Name already exists, will overwrite\n");
+        exists = true;
     }
 
-    print("Finalising upload\n");
-    size_t dig_len = 64;
-    var digest = new uint8[64];
-    full_checksum.get_digest(digest, ref dig_len);
-    digest.length = (int)dig_len;
-
-    var fmessage = new Pprf.Messages.FinaliseUpload();
-    fmessage.collection_id = collection_id;
-    fmessage.destination = file_name;
-    fmessage.authenticate(session.session_authentication, digest, creds);
-    //  fmessage.bspatch_old_checksum = new Invercargill.BinaryData.from_base64("2+1VxEx6bMUfJ0g0b3vnJwDFVf/acGG5IXENvb3ElCFU9dFXi+6muFb2GXUVA8B5+PV2AWiOEKhD3RqNTYwBpw==").to_array();
-    fmessage.flags = Pprf.Messages.FinaliseUploadFlags.OVERWRITE_DESTINATION;
-
-    response = client.send_message(fmessage);
-    if(response is Pprf.Messages.Failure) {
-        print(@"Failure $(response.code): $(response.message)\n");
-        return -1;
-    }
+    print("Beginning upload\n");
+    var file_stream = upload_file.read();
+    var flags = exists ? Pprf.Messages.FinaliseUploadFlags.OVERWRITE_DESTINATION : 0;
+    client.upload(collection_id, file_stream, file_size, file_name, exists, member, null, flags);
 
-    print("Publishing publication\n");
-    
+    print("Computing publication signature\n");
+    var digest = Pprf.Util.file_checksum(upload_file);
     var publication = new Ppub.CollectionPublication(file_name, new DateTime.now_local(), member.name, creds, digest);
 
-    var pmessage = new Pprf.Messages.Publish();
-    pmessage.collection_id = collection_id;
-    pmessage.publication_string = publication.to_string();
-
-    response = client.send_authenticated_message(pmessage, member);
-    if(response is Pprf.Messages.Failure) {
-        print(@"Failure $(response.code): $(response.message)\n");
-        return -1;
-    }
+    print("Publishing publication\n");
+    client.publish(collection_id, publication, member);
     
     print("Done\n");
     return 0;