Compare commits

..

No commits in common. "master" and "xethyrion-master" have entirely different histories.

240 changed files with 1802 additions and 22948 deletions

View file

@ -21,20 +21,15 @@
<builder id="cdt.managedbuild.target.gnu.builder.base.1870748436" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.base"/>
<tool id="cdt.managedbuild.tool.gnu.archiver.base.1038880733" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/>
<tool id="cdt.managedbuild.tool.gnu.cpp.compiler.base.1701788660" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.base">
<option id="gnu.cpp.compiler.option.include.paths.776431500" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/c-libp2p/include}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/c-multiaddr/include}&quot;"/>
</option>
<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1773271782" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
</tool>
<tool id="cdt.managedbuild.tool.gnu.c.compiler.base.365209117" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.base">
<option id="gnu.c.compiler.option.include.paths.106437885" name="Include paths (-I)" superClass="gnu.c.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/c-libp2p/include}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/c-multihash/include}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/c-multiaddr}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/lmdb}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/c-ipfs/include}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/c-protobuf}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/c-libp2p/include}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/c-multiaddr/include}&quot;"/>
</option>
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.581176638" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
</tool>
@ -46,10 +41,6 @@
</inputType>
</tool>
<tool id="cdt.managedbuild.tool.gnu.assembler.base.451195806" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.base">
<option id="gnu.both.asm.option.include.paths.1115343131" name="Include paths (-I)" superClass="gnu.both.asm.option.include.paths" valueType="includePath">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/c-libp2p/include}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/c-multiaddr/include}&quot;"/>
</option>
<inputType id="cdt.managedbuild.tool.gnu.assembler.input.1339940304" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
</tool>
</toolChain>
@ -81,6 +72,7 @@
<buildTargets>
<target name="all" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>make</buildCommand>
<buildArguments/>
<buildTarget>all</buildTarget>
<stopOnError>true</stopOnError>
<useDefaultCommand>true</useDefaultCommand>
@ -88,6 +80,7 @@
</target>
<target name="clean" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>make</buildCommand>
<buildArguments/>
<buildTarget>clean</buildTarget>
<stopOnError>true</stopOnError>
<useDefaultCommand>true</useDefaultCommand>
@ -95,6 +88,7 @@
</target>
<target name="rebuild" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>make</buildCommand>
<buildArguments/>
<buildTarget>rebuild</buildTarget>
<stopOnError>true</stopOnError>
<useDefaultCommand>true</useDefaultCommand>
@ -102,39 +96,13 @@
</target>
<target name="clean" path="thirdparty" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>make</buildCommand>
<buildArguments/>
<buildTarget>clean</buildTarget>
<stopOnError>true</stopOnError>
<useDefaultCommand>false</useDefaultCommand>
<runAllBuilders>true</runAllBuilders>
</target>
<target name="all" path="test" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>make</buildCommand>
<buildArguments/>
<buildTarget>all</buildTarget>
<stopOnError>true</stopOnError>
<useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>true</runAllBuilders>
</target>
<target name="clean" path="test" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>make</buildCommand>
<buildArguments/>
<buildTarget>clean</buildTarget>
<stopOnError>true</stopOnError>
<useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>true</runAllBuilders>
</target>
<target name="rebuild" path="test" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>make</buildCommand>
<buildArguments/>
<buildTarget>rebuild</buildTarget>
<stopOnError>true</stopOnError>
<useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>true</runAllBuilders>
</target>
<target name="clean" path="thirdparty/ipfsaddr" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>make</buildCommand>
<buildArguments/>
<buildTarget>clean</buildTarget>
<stopOnError>true</stopOnError>
<useDefaultCommand>false</useDefaultCommand>

3
.github/FUNDING.yml vendored
View file

@ -1,3 +0,0 @@
# These are supported funding model platforms
custom: https://www.blockchain.com/btc/payment_request?address=1AFGT5gVj7xhfjgHTuwEoaV56WTCh7Gjf1#BITCOIN_ONLY

7
.gitignore vendored
View file

@ -7,10 +7,3 @@
*.o
.settings/language.settings.xml
test/test_ipfs
main/ipfs
test/test1.txt
test/test2.txt
test/scripts/hello.bin
test/scripts/hello2.bin
test/scripts/testlog.txt
test/scripts/generate_file

7
.gitmodules vendored
View file

@ -1,7 +0,0 @@
[submodule "c-libp2p"]
path = c-libp2p
url = https://github.com/Agorise/c-libp2p.git
[submodule "lmdb"]
path = lmdb
url = https://github.com/jmjatlanta/lmdb.git
branch = mdb.master

View file

@ -1,9 +1,6 @@
MIT License
Copyright (c) 2019 AGORISE, LTD.
An International Business Company, Cyprus Reg# ΗΕ375959
Contains works from BitShares Munich IVS
Copyright (c) 2016 BitShares Munich IVS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -3,57 +3,31 @@ DEBUG = true
export DEBUG
all:
cd c-libp2p; make all;
cd lmdb/libraries/liblmdb; make all;
cd blocks; make all;
cd cid; make all;
cd cmd; make all;
cd commands; make all;
cd core; make all;
cd exchange; make all;
cd importer; make all;
cd journal; make all;
cd merkledag; make all;
cd multibase; make all;
cd pin; make all;
cd os; make all;
cd repo; make all;
cd flatfs; make all;
cd datastore; make all;
cd thirdparty; make all;
cd unixfs; make all;
cd routing; make all;
cd dnslink; make all;
cd namesys; make all;
cd path; make all;
cd util; make all;
cd main; make all;
cd test; make all;
clean:
cd c-libp2p; make clean;
cd lmdb/libraries/liblmdb; make clean;
cd blocks; make clean;
cd cid; make clean;
cd cmd; make clean;
cd commands; make clean;
cd core; make clean;
cd exchange; make clean;
cd importer; make clean;
cd journal; make clean;
cd merkledag; make clean;
cd multibase; make clean;
cd pin; make clean;
cd os; make clean;
cd repo; make clean;
cd flatfs; make clean;
cd datastore; make clean;
cd thirdparty; make clean;
cd unixfs; make clean;
cd main; make clean;
cd routing; make clean;
cd dnslink; make clean;
cd namesys; make clean;
cd path; make clean;
cd util; make clean;
cd test; make clean;
rebuild: clean all

View file

@ -1,28 +1,7 @@
# C-IPFS
IPFS implementation in C, (not just an API client library).
## Quick start for users:
* **ipfs init** to create an ipfs repository on your machine
* **ipfs add MyFile.txt** to add a file to the repository, will return with a hash that can be used to retrieve the file.
* **ipfs cat _hash_** to retrieve a file from the repository
## For techies (ipfs spec docs):
* [getting started](https://github.com/ipfs/specs/blob/master/overviews/implement-ipfs.md)
* [specifications](https://github.com/ipfs/specs)
* [getting started](https://github.com/ipfs/community/issues/177)
* [libp2p](https://github.com/libp2p/specs)
## Prerequisites: To compile the C version you will need, all included as submodules:
* [lmdb](https://github.com/jmjatlanta/lmdb)
* [c-protobuf](https://github.com/Agorise/c-protobuf)
* [c-multihash](https://github.com/Agorise/c-multihash)
* [c-multiaddr](https://github.com/Agorise/c-multiaddr)
* [c-libp2p](https://github.com/Agorise/c-libp2p)
And of course this project at https://github.com/Agorise/c-ipfs
## How to compile the C version:
```
git submodule update --init --recursive
make all
```
# c-ipfs
IPFS implementation in C, (not just an API client library).<br>
<br>
getting started: https://github.com/ipfs/specs/blob/master/overviews/implement-ipfs.md <br>
specifications: https://github.com/ipfs/specs <br>
getting started: https://github.com/ipfs/community/issues/177 <br>
libp2p: https://github.com/libp2p/specs <br>

View file

@ -1,5 +1,5 @@
CC = gcc
CFLAGS = -O0 -I../include -I../c-libp2p/include -I../c-libp2p/c-multihash/include -I../c-libp2p/c-multiaddr/include -I../c-libp2p/c-protobuf -Wall -std=gnu99
CFLAGS = -O0 -I../include -I../../c-libp2p/include -I../../c-multihash/include -I../../c-multiaddr/include
ifdef DEBUG
CFLAGS += -g3

View file

@ -9,105 +9,6 @@
#include "ipfs/blocks/block.h"
#include "ipfs/cid/cid.h"
/***
* The protobuf functions
*/
// protobuf fields: data & data_length cid
enum WireType ipfs_block_message_fields[] = { WIRETYPE_LENGTH_DELIMITED, WIRETYPE_LENGTH_DELIMITED};
/**
* Determine the approximate size of an encoded block
* @param block the block to measure
* @returns the approximate size needed to encode the protobuf
*/
size_t ipfs_blocks_block_protobuf_encode_size(const struct Block* block) {
return 22 + ipfs_cid_protobuf_encode_size(block->cid) + block->data_length;
}
/**
* Encode the Block into protobuf format
* @param block the block to encode
* @param buffer the buffer to fill
* @param max_buffer_size the max size of the buffer
* @param bytes_written the number of bytes used
* @returns true(1) on success
*/
int ipfs_blocks_block_protobuf_encode(const struct Block* block, unsigned char* buffer, size_t max_buffer_length, size_t* bytes_written) {
// data & data_size
size_t bytes_used = 0;
*bytes_written = 0;
int retVal = 0;
retVal = protobuf_encode_length_delimited(1, ipfs_block_message_fields[0], (char*)block->data, block->data_length, &buffer[*bytes_written], max_buffer_length - *bytes_written, &bytes_used);
*bytes_written += bytes_used;
// cid
size_t cid_size = ipfs_cid_protobuf_encode_size(block->cid);
unsigned char cid[cid_size];
retVal = ipfs_cid_protobuf_encode(block->cid, cid, cid_size, &cid_size);
if (retVal == 0)
return 0;
retVal = protobuf_encode_length_delimited(2, ipfs_block_message_fields[1], (char*)cid, cid_size, &buffer[*bytes_written], max_buffer_length - *bytes_written, &bytes_used);
if (retVal == 0)
return 0;
*bytes_written += bytes_used;
return 1;
}
/***
* Decode from a protobuf stream into a Block struct
* @param buffer the buffer to pull from
* @param buffer_length the length of the buffer
* @param block the block to fill
* @returns true(1) on success
*/
int ipfs_blocks_block_protobuf_decode(const unsigned char* buffer, const size_t buffer_length, struct Block** block) {
size_t pos = 0;
int retVal = 0;
unsigned char* temp_buffer = NULL;
size_t temp_size;
*block = ipfs_block_new();
if (*block == NULL)
goto exit;
while(pos < buffer_length) {
size_t bytes_read = 0;
int field_no;
enum WireType field_type;
if (protobuf_decode_field_and_type(&buffer[pos], buffer_length, &field_no, &field_type, &bytes_read) == 0) {
goto exit;
}
pos += bytes_read;
switch(field_no) {
case (1): // data
if (protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&((*block)->data), &((*block)->data_length), &bytes_read) == 0)
goto exit;
pos += bytes_read;
break;
case (2): // cid
if (protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&temp_buffer, &temp_size, &bytes_read) == 0)
goto exit;
pos += bytes_read;
if (ipfs_cid_protobuf_decode(temp_buffer, temp_size, &((*block)->cid)) == 0)
goto exit;
free(temp_buffer);
temp_buffer = NULL;
break;
}
}
retVal = 1;
exit:
if (retVal == 0) {
ipfs_block_free(*block);
}
if (temp_buffer != NULL)
free(temp_buffer);
return retVal;
}
/***
* Create a new block based on the incoming data
* @param data the data to base the block on
@ -115,40 +16,35 @@ exit:
* @param block a pointer to the struct Block that will be created
* @returns true(1) on success
*/
struct Block* ipfs_block_new() {
int ipfs_blocks_block_new(const unsigned char* data, size_t data_size, struct Block** block) {
// allocate memory for structure
struct Block* block = (struct Block*)malloc(sizeof(struct Block));
if ( block == NULL)
(*block) = (struct Block*)malloc(sizeof(struct Block));
if ((*block) == NULL)
return 0;
block->data = NULL;
block->data_length = 0;
block->cid = NULL;
return block;
}
int ipfs_blocks_block_add_data(const unsigned char* data, size_t data_size, struct Block* block) {
// cid
unsigned char hash[32];
if (libp2p_crypto_hashing_sha256(data, data_size, &hash[0]) == 0) {
free(*block);
return 0;
}
block->cid = ipfs_cid_new(0, hash, 32, CID_DAG_PROTOBUF);
if (block->cid == NULL) {
if (ipfs_cid_new(0, hash, 32, CID_PROTOBUF, &((*block)->cid)) == 0) {
free(*block);
return 0;
}
block->data_length = data_size;
(*block)->data_length = data_size;
block->data = malloc(sizeof(unsigned char) * data_size);
if ( block->data == NULL) {
ipfs_cid_free(block->cid);
(*block)->data = malloc(sizeof(unsigned char) * data_size);
if ( (*block)->data == NULL) {
ipfs_cid_free((*block)->cid);
free(*block);
return 0;
}
memcpy( block->data, data, data_size);
memcpy( (*block)->data, data, data_size);
return 1;
}
@ -157,37 +53,10 @@ int ipfs_blocks_block_add_data(const unsigned char* data, size_t data_size, stru
* @param block the block to free
* @returns true(1) on success
*/
int ipfs_block_free(struct Block* block) {
if (block != NULL) {
if (block->cid != NULL)
ipfs_cid_free(block->cid);
if (block->data != NULL)
free(block->data);
free(block);
}
int ipfs_blocks_block_free(struct Block* block) {
ipfs_cid_free(block->cid);
if (block->data != NULL)
free(block->data);
free(block);
return 1;
}
/***
* Make a copy of a block
* @param original the original
* @returns a new Block that is a copy
*/
struct Block* ipfs_block_copy(struct Block* original) {
struct Block* copy = ipfs_block_new();
if (copy != NULL) {
copy->data_length = original->data_length;
copy->data = (unsigned char*) malloc(original->data_length);
if (copy->data == NULL) {
ipfs_block_free(copy);
return NULL;
}
memcpy(copy->data, original->data, original->data_length);
copy->cid = ipfs_cid_copy(original->cid);
if (copy->cid == NULL) {
ipfs_block_free(copy);
return NULL;
}
}
return copy;
}

View file

@ -4,54 +4,15 @@
#include "libp2p/crypto/encoding/base32.h"
#include "ipfs/cid/cid.h"
#include "ipfs/blocks/block.h"
#include "ipfs/blocks/blockstore.h"
#include "ipfs/datastore/ds_helper.h"
#include "ipfs/repo/fsrepo/fs_repo.h"
#include "libp2p/os/utils.h"
/***
* Create a new Blockstore struct
* @param fs_repo the FSRepo to use
* @returns the new Blockstore struct, or NULL if there was a problem.
*/
struct Blockstore* ipfs_blockstore_new(const struct FSRepo* fs_repo) {
struct Blockstore* blockstore = (struct Blockstore*) malloc(sizeof(struct Blockstore));
if(blockstore != NULL) {
blockstore->blockstoreContext = (struct BlockstoreContext*) malloc(sizeof(struct BlockstoreContext));
if (blockstore->blockstoreContext == NULL) {
free(blockstore);
return NULL;
}
blockstore->blockstoreContext->fs_repo = fs_repo;
blockstore->Delete = ipfs_blockstore_delete;
blockstore->Get = ipfs_blockstore_get;
blockstore->Has = ipfs_blockstore_has;
blockstore->Put = ipfs_blockstore_put;
}
return blockstore;
}
/**
* Release resources of a Blockstore struct
* @param blockstore the struct to free
* @returns true(1)
*/
int ipfs_blockstore_free(struct Blockstore* blockstore) {
if (blockstore != NULL) {
if (blockstore->blockstoreContext != NULL)
free(blockstore->blockstoreContext);
free(blockstore);
}
return 1;
}
/**
* Delete a block based on its Cid
* @param cid the Cid to look for
* @param returns true(1) on success
*/
int ipfs_blockstore_delete(const struct BlockstoreContext* context, struct Cid* cid) {
int ipfs_blockstore_delete(struct Cid* cid, struct FSRepo* fs_repo) {
return 0;
}
@ -60,88 +21,18 @@ int ipfs_blockstore_delete(const struct BlockstoreContext* context, struct Cid*
* @param cid the Cid to look for
* @returns true(1) if found
*/
int ipfs_blockstore_has(const struct BlockstoreContext* context, struct Cid* cid) {
int ipfs_blockstore_has(struct Cid* cid, struct FSRepo* fs_repo) {
return 0;
}
unsigned char* ipfs_blockstore_cid_to_base32(const struct Cid* cid) {
size_t key_length = libp2p_crypto_encoding_base32_encode_size(cid->hash_length);
unsigned char* buffer = (unsigned char*)malloc(key_length + 1);
if (buffer == NULL)
return NULL;
int retVal = ipfs_datastore_helper_ds_key_from_binary(cid->hash, cid->hash_length, &buffer[0], key_length, &key_length);
if (retVal == 0) {
free(buffer);
return NULL;
}
buffer[key_length] = 0;
return buffer;
}
unsigned char* ipfs_blockstore_hash_to_base32(const unsigned char* hash, size_t hash_length) {
size_t key_length = libp2p_crypto_encoding_base32_encode_size(hash_length);
unsigned char* buffer = (unsigned char*)malloc(key_length + 1);
if (buffer == NULL)
return NULL;
int retVal = ipfs_datastore_helper_ds_key_from_binary(hash, hash_length, &buffer[0], key_length, &key_length);
if (retVal == 0) {
free(buffer);
return NULL;
}
buffer[key_length] = 0;
return buffer;
}
char* ipfs_blockstore_path_get(const struct FSRepo* fs_repo, const char* filename) {
int filepath_size = strlen(fs_repo->path) + 12;
char filepath[filepath_size];
int retVal = os_utils_filepath_join(fs_repo->path, "blockstore", filepath, filepath_size);
if (retVal == 0) {
free(filepath);
return 0;
}
int complete_filename_size = strlen(filepath) + strlen(filename) + 2;
char* complete_filename = (char*)malloc(complete_filename_size);
if (complete_filename == NULL)
return NULL;
retVal = os_utils_filepath_join(filepath, filename, complete_filename, complete_filename_size);
return complete_filename;
}
/***
* Find a block based on its Cid
* @param cid the Cid to look for
* @param block where to put the data to be returned
* @returns true(1) on success
*/
int ipfs_blockstore_get(const struct BlockstoreContext* context, struct Cid* cid, struct Block** block) {
int retVal = 0;
// get datastore key, which is a base32 key of the multihash
unsigned char* key = ipfs_blockstore_hash_to_base32(cid->hash, cid->hash_length);
char* filename = ipfs_blockstore_path_get(context->fs_repo, (char*)key);
size_t file_size = os_utils_file_size(filename);
unsigned char buffer[file_size];
FILE* file = fopen(filename, "rb");
if (file == NULL)
goto exit;
size_t bytes_read = fread(buffer, 1, file_size, file);
fclose(file);
if (!ipfs_blocks_block_protobuf_decode(buffer, bytes_read, block))
goto exit;
(*block)->cid = ipfs_cid_copy(cid);
retVal = 1;
exit:
free(key);
free(filename);
return retVal;
int ipfs_blockstore_get(struct Cid* cid, struct Block* block, struct FSRepo* fs_repo) {
return 0;
}
/***
@ -149,217 +40,16 @@ int ipfs_blockstore_get(const struct BlockstoreContext* context, struct Cid* cid
* @param block the block to store
* @returns true(1) on success
*/
int ipfs_blockstore_put(const struct BlockstoreContext* context, struct Block* block, size_t* bytes_written) {
int ipfs_blockstore_put(struct Block* block, struct FSRepo* fs_repo) {
// from blockstore.go line 118
int retVal = 0;
// Get Datastore key, which is a base32 key of the multihash,
unsigned char* key = ipfs_blockstore_cid_to_base32(block->cid);
if (key == NULL) {
free(key);
// Get Datastore key, which is a base32 key of the binary,
size_t key_length = libp2p_crypto_encoding_base32_encode_size(block->data_length);
unsigned char key[key_length];
int retVal = ipfs_datastore_helper_ds_key_from_binary(block->data, block->data_length, &key[0], key_length, &key_length);
if (retVal == 0)
return 0;
}
//TODO: put this in subdirectories
// turn the block into a binary array
size_t protobuf_len = ipfs_blocks_block_protobuf_encode_size(block);
unsigned char protobuf[protobuf_len];
retVal = ipfs_blocks_block_protobuf_encode(block, protobuf, protobuf_len, &protobuf_len);
if (retVal == 0) {
free(key);
return 0;
}
// now write byte array to file
char* filename = ipfs_blockstore_path_get(context->fs_repo, (char*)key);
if (filename == NULL) {
free(key);
return 0;
}
FILE* file = fopen(filename, "wb");
*bytes_written = fwrite(protobuf, 1, protobuf_len, file);
fclose(file);
if (*bytes_written != protobuf_len) {
free(key);
free(filename);
return 0;
}
// send to Put with key (this is now done separately)
//fs_repo->config->datastore->datastore_put(key, key_length, block->data, block->data_length, fs_repo->config->datastore);
free(key);
free(filename);
return 1;
// send to Put with key
fs_repo->config->datastore->datastore_put(key, key_length, block, fs_repo->config->datastore);
return 0;
}
/***
* Put a struct UnixFS in the blockstore
* @param unix_fs the structure
* @param fs_repo the repo to place the strucure in
* @param bytes_written the number of bytes written to the blockstore
* @returns true(1) on success
*/
int ipfs_blockstore_put_unixfs(const struct UnixFS* unix_fs, const struct FSRepo* fs_repo, size_t* bytes_written) {
// from blockstore.go line 118
int retVal = 0;
// Get Datastore key, which is a base32 key of the multihash,
unsigned char* key = ipfs_blockstore_hash_to_base32(unix_fs->hash, unix_fs->hash_length);
if (key == NULL) {
free(key);
return 0;
}
//TODO: put this in subdirectories
// turn the block into a binary array
size_t protobuf_len = ipfs_unixfs_protobuf_encode_size(unix_fs);
unsigned char protobuf[protobuf_len];
retVal = ipfs_unixfs_protobuf_encode(unix_fs, protobuf, protobuf_len, &protobuf_len);
if (retVal == 0) {
free(key);
return 0;
}
// now write byte array to file
char* filename = ipfs_blockstore_path_get(fs_repo, (char*)key);
if (filename == NULL) {
free(key);
return 0;
}
FILE* file = fopen(filename, "wb");
*bytes_written = fwrite(protobuf, 1, protobuf_len, file);
fclose(file);
if (*bytes_written != protobuf_len) {
free(key);
free(filename);
return 0;
}
free(key);
free(filename);
return 1;
}
/***
* Find a UnixFS struct based on its hash
* @param hash the hash to look for
* @param hash_length the length of the hash
* @param unix_fs the struct to fill
* @param fs_repo where to look for the data
* @returns true(1) on success
*/
int ipfs_blockstore_get_unixfs(const unsigned char* hash, size_t hash_length, struct UnixFS** block, const struct FSRepo* fs_repo) {
// get datastore key, which is a base32 key of the multihash
unsigned char* key = ipfs_blockstore_hash_to_base32(hash, hash_length);
char* filename = ipfs_blockstore_path_get(fs_repo, (char*)key);
size_t file_size = os_utils_file_size(filename);
unsigned char buffer[file_size];
FILE* file = fopen(filename, "rb");
size_t bytes_read = fread(buffer, 1, file_size, file);
fclose(file);
int retVal = ipfs_unixfs_protobuf_decode(buffer, bytes_read, block);
free(key);
free(filename);
return retVal;
}
/***
* Put a struct Node in the blockstore
* @param node the structure
* @param fs_repo the repo to place the strucure in
* @param bytes_written the number of bytes written to the blockstore
* @returns true(1) on success
*/
int ipfs_blockstore_put_node(const struct HashtableNode* node, const struct FSRepo* fs_repo, size_t* bytes_written) {
// from blockstore.go line 118
int retVal = 0;
// Get Datastore key, which is a base32 key of the multihash,
unsigned char* key = ipfs_blockstore_hash_to_base32(node->hash, node->hash_size);
if (key == NULL) {
free(key);
return 0;
}
//TODO: put this in subdirectories
// turn the block into a binary array
size_t protobuf_len = ipfs_hashtable_node_protobuf_encode_size(node);
unsigned char protobuf[protobuf_len];
retVal = ipfs_hashtable_node_protobuf_encode(node, protobuf, protobuf_len, &protobuf_len);
if (retVal == 0) {
free(key);
return 0;
}
// now write byte array to file
char* filename = ipfs_blockstore_path_get(fs_repo, (char*)key);
if (filename == NULL) {
free(key);
return 0;
}
FILE* file = fopen(filename, "wb");
*bytes_written = fwrite(protobuf, 1, protobuf_len, file);
fclose(file);
if (*bytes_written != protobuf_len) {
free(key);
free(filename);
return 0;
}
free(key);
free(filename);
return 1;
}
/***
* Find a UnixFS struct based on its hash
* @param hash the hash to look for
* @param hash_length the length of the hash
* @param unix_fs the struct to fill
* @param fs_repo where to look for the data
* @returns true(1) on success
*/
int ipfs_blockstore_get_node(const unsigned char* hash, size_t hash_length, struct HashtableNode** node, const struct FSRepo* fs_repo) {
// get datastore key, which is a base32 key of the multihash
unsigned char* key = ipfs_blockstore_hash_to_base32(hash, hash_length);
char* filename = ipfs_blockstore_path_get(fs_repo, (char*)key);
size_t file_size = os_utils_file_size(filename);
unsigned char buffer[file_size];
FILE* file = fopen(filename, "rb");
size_t bytes_read = fread(buffer, 1, file_size, file);
fclose(file);
// now we have the block, convert it to a node
struct Block* block;
if (!ipfs_blocks_block_protobuf_decode(buffer, bytes_read, &block)) {
free(key);
free(filename);
ipfs_block_free(block);
return 0;
}
int retVal = ipfs_hashtable_node_protobuf_decode(block->data, block->data_length, node);
free(key);
free(filename);
ipfs_block_free(block);
return retVal;
}

@ -1 +0,0 @@
Subproject commit d0c319a88cd1f2cb3a219420b5a0b930e72562e2

View file

@ -1,5 +1,5 @@
CC = gcc
CFLAGS = -O0 -I../include -I../c-libp2p/include -I../c-libp2p/c-multihash/include -I../c-libp2p/c-multiaddr/include -I../c-libp2p/c-protobuf -Wall -std=gnu99
CFLAGS = -O0 -I../include -I../../c-libp2p/include -I../../c-multihash/include -I../../c-multiaddr/include
ifdef DEBUG
CFLAGS += -g3
@ -7,7 +7,7 @@ endif
LFLAGS =
DEPS =
OBJS = cid.o set.o
OBJS = cid.o
%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)

277
cid/cid.c
View file

@ -9,114 +9,38 @@
#include "ipfs/cid/cid.h"
#include "libp2p/crypto/encoding/base58.h"
#include "ipfs/multibase/multibase.h"
#include "mh/hashes.h"
#include "mh/multihash.h"
#include "varint.h"
#include "multiaddr/varint.h"
enum WireType ipfs_cid_message_fields[] = { WIRETYPE_VARINT, WIRETYPE_VARINT, WIRETYPE_LENGTH_DELIMITED };
size_t ipfs_cid_protobuf_encode_size(const struct Cid* cid) {
if (cid != NULL)
return 11+12+cid->hash_length+11;
return 0;
}
int ipfs_cid_protobuf_encode(const struct Cid* cid, unsigned char* buffer, size_t buffer_length, size_t* bytes_written) {
size_t bytes_used;
*bytes_written = 0;
int retVal = 0;
if (cid != NULL) {
retVal = protobuf_encode_varint(1, ipfs_cid_message_fields[0], cid->version, buffer, buffer_length, &bytes_used);
if (retVal == 0)
return 0;
*bytes_written += bytes_used;
retVal = protobuf_encode_varint(2, ipfs_cid_message_fields[1], cid->codec, &buffer[*bytes_written], buffer_length - (*bytes_written), &bytes_used);
if (retVal == 0)
return 0;
*bytes_written += bytes_used;
retVal = protobuf_encode_length_delimited(3, ipfs_cid_message_fields[2], (char*)cid->hash, cid->hash_length, &buffer[*bytes_written], buffer_length - (*bytes_written), &bytes_used);
if (retVal == 0)
return 0;
*bytes_written += bytes_used;
}
return 1;
}
int ipfs_cid_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct Cid** output) {
// short cut for nulls
if (buffer_length == 0) {
*output = NULL;
return 1;
}
size_t pos = 0;
int version = 0;
unsigned char* hash;
size_t hash_length;
char codec = 0;
int retVal = 0;
while(pos < buffer_length) {
size_t bytes_read = 0;
int field_no;
enum WireType field_type;
if (protobuf_decode_field_and_type(&buffer[pos], buffer_length, &field_no, &field_type, &bytes_read) == 0) {
return 0;
}
pos += bytes_read;
switch(field_no) {
case (1):
version = varint_decode(&buffer[pos], buffer_length - pos, &bytes_read);
pos += bytes_read;
break;
case (2):
codec = varint_decode(&buffer[pos], buffer_length - pos, &bytes_read);
pos += bytes_read;
break;
case (3):
retVal = protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&hash, &hash_length, &bytes_read);
if (retVal == 0)
return 0;
pos += bytes_read;
break;
}
}
*output = ipfs_cid_new(version, hash, hash_length, codec);
retVal = *output != NULL;
free(hash);
return retVal;
}
/**
* Create a new CID based on the given hash
* @param version the version
* @param hash the multihash
* @param hash_length the length of the multihash in bytes
* @param codec the codec to be used (NOTE: For version 0, this should be CID_DAG_PROTOBUF)
* @returns the new Cid or NULL if there was a problem
* @param codec the codec to be used (NOTE: For version 0, this should be CID_PROTOBUF)
* @param cid where to put the results
* @returns true(1) on success
*/
struct Cid* ipfs_cid_new(int version, const unsigned char* hash, size_t hash_length, const char codec) {
struct Cid* cid = (struct Cid*) malloc(sizeof(struct Cid));
if (cid != NULL) {
cid->hash_length = hash_length;
if (hash_length == 0 || hash == NULL) {
cid->hash = NULL;
} else {
cid->hash = (unsigned char*) malloc(sizeof(unsigned char) * hash_length);
if (cid->hash == NULL) {
free(cid);
return NULL;
}
memcpy(cid->hash, hash, hash_length);
}
// assign other values
cid->version = version;
cid->codec = codec;
int ipfs_cid_new(int version, unsigned char* hash, size_t hash_length, const char codec, struct Cid** ptrToCid) {
// allocate memory
*ptrToCid = (struct Cid*)malloc(sizeof(struct Cid));
struct Cid* cid = *ptrToCid;
if (cid == NULL)
return 0;
cid->hash = malloc(sizeof(unsigned char) * hash_length);
if (cid->hash == NULL) {
free(cid);
return 0;
}
return cid;
// assign values
cid->version = version;
cid->codec = codec;
memcpy(cid->hash, hash, hash_length);
cid->hash_length = hash_length;
return 1;
}
/***
@ -125,63 +49,20 @@ struct Cid* ipfs_cid_new(int version, const unsigned char* hash, size_t hash_len
* @returns 1
*/
int ipfs_cid_free(struct Cid* cid) {
if (cid != NULL) {
if (cid->hash != NULL) {
free(cid->hash);
cid->hash = NULL;
}
free(cid);
}
if (cid->hash != NULL)
free(cid->hash);
free(cid);
return 1;
}
/***
* Make a copy of a Cid
* @param original the original
* @returns a copy of the original
*/
struct Cid* ipfs_cid_copy(const struct Cid* original) {
struct Cid* copy = (struct Cid*) malloc(sizeof(struct Cid));
if (copy != NULL) {
copy->codec = original->codec;
copy->version = original->version;
copy->hash_length = original->hash_length;
copy->hash = (unsigned char*) malloc(original->hash_length);
if (copy->hash == NULL) {
ipfs_cid_free(copy);
return NULL;
}
memcpy(copy->hash, original->hash, original->hash_length);
}
return copy;
}
/***
* Create a CID from an ipfs or ipns string (i.e. "/ipns/QmAb12CD..."
* @param incoming the incoming string
* @param cid the resultant Cid
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_cid_decode_hash_from_ipfs_ipns_string(const char* incoming, struct Cid** cid) {
if (incoming == NULL)
return 0;
if (strstr(incoming, "/ipfs/") != incoming && strstr(incoming, "/ipns/") != incoming)
return 0;
const char* base58 = &incoming[6];
char* slash = strstr(incoming, "/");
if (slash != NULL)
slash[0] = '\0';
return ipfs_cid_decode_hash_from_base58((unsigned char*)base58, strlen(base58), cid);
}
/***
* Fill a Cid struct based on a base 58 encoded multihash
* Fill a Cid struct based on a base 58 encoded string
* @param incoming the string
* @param incoming_size the size of the string
* @cid the Cid struct to fill
* @return true(1) on success
*/
int ipfs_cid_decode_hash_from_base58(const unsigned char* incoming, size_t incoming_length, struct Cid** cid) {
int ipfs_cid_decode_from_string(const unsigned char* incoming, size_t incoming_length, struct Cid** cid) {
int retVal = 0;
if (incoming_length < 2)
@ -196,8 +77,7 @@ int ipfs_cid_decode_hash_from_base58(const unsigned char* incoming, size_t incom
if (retVal == 0)
return 0;
// now we have the hash, build the object
*cid = ipfs_cid_new(0, &hash[2], hash_length - 2, CID_DAG_PROTOBUF);
return *cid != NULL;
return ipfs_cid_new(0, hash, hash_length, CID_PROTOBUF, cid);
}
// TODO: finish this
@ -219,65 +99,17 @@ int ipfs_cid_decode_hash_from_base58(const unsigned char* incoming, size_t incom
return 0;
}
/**
* Turn a cid hash into a base 58
* @param hash the hash to work with
* @param hash_length the length of the existing hash
* @param buffer where to put the results
* @param max_buffer_length the maximum space reserved for the results
* @returns true(1) on success
*/
int ipfs_cid_hash_to_base58(const unsigned char* hash, size_t hash_length, unsigned char* buffer, size_t max_buffer_length) {
int multihash_len = hash_length + 2;
unsigned char multihash[multihash_len];
if (mh_new(multihash, MH_H_SHA2_256, hash, hash_length) < 0) {
return 0;
}
// base58
size_t b58_size = libp2p_crypto_encoding_base58_encode_size(multihash_len);
if (b58_size > max_buffer_length) // buffer too small
return 0;
if( libp2p_crypto_encoding_base58_encode(multihash, multihash_len, &buffer, &max_buffer_length) == 0) {
return 0;
}
return 1;
}
/***
* Turn the hash of this CID into a c string
* @param cid the cid
* @param result a place to allocate and store the string
* @returns a pointer to the string (*result) or NULL if there was a problem
*/
char* ipfs_cid_to_string(const struct Cid* cid, char **result) {
size_t str_len = libp2p_crypto_encoding_base58_encode_size(cid->hash_length) + 1;
char *str = (char*) malloc(str_len);
*result = str;
if (str != NULL) {
if (!libp2p_crypto_encoding_base58_encode(cid->hash, cid->hash_length, (unsigned char**)&str, &str_len)) {
free(str);
str = NULL;
}
}
return str;
}
/***
* Turn a multibase decoded string of bytes into a Cid struct
* @param incoming the multibase decoded array
* @param incoming_size the size of the array
* @param cid the Cid structure to fill
*/
int ipfs_cid_cast(const unsigned char* incoming, size_t incoming_size, struct Cid* cid) {
int ipfs_cid_cast(unsigned char* incoming, size_t incoming_size, struct Cid* cid) {
// this is a multihash
if (incoming_size == 34 && incoming[0] == 18 && incoming[1] == 32) {
// this is a multihash
cid->hash_length = mh_multihash_length(incoming, incoming_size);
cid->codec = CID_DAG_PROTOBUF;
cid->codec = CID_PROTOBUF;
cid->version = 0;
mh_multihash_digest(incoming, incoming_size, &cid->hash, &cid->hash_length);
@ -286,57 +118,22 @@ int ipfs_cid_cast(const unsigned char* incoming, size_t incoming_size, struct Ci
// This is not a multihash. Perhaps it is using varints. Try to peel the information out of the bytes.
// first the version
int pos = 0;
int pos = 0, retVal = 0;
size_t num_bytes = 0;
cid->version = varint_decode(&incoming[pos], incoming_size - pos, &num_bytes);
if (num_bytes == 0 || cid->version > 1 || cid->version < 0)
num_bytes = uvarint_decode32(&incoming[pos], incoming_size - pos, &cid->version);
if (num_bytes < 0 || cid->version > 1 || cid->version < 0)
return 0;
pos = num_bytes;
// now the codec
uint32_t codec = 0;
codec = varint_decode(&incoming[pos], incoming_size - pos, &num_bytes);
if (num_bytes == 0)
num_bytes = uvarint_decode32(&incoming[pos], incoming_size - pos, &codec);
if (num_bytes < 0)
return 0;
cid->codec = codec;
pos += num_bytes;
// now what is left
cid->hash_length = incoming_size - pos;
cid->hash = (unsigned char*)(&incoming[pos]);
cid->hash = &incoming[pos];
return 1;
}
/**
* Compare two cids
*
* TODO: find a common denominator between versions and codecs so that
* we can compare apples to apples.
*
* @param a side A
* @param b side B
* @returns < 0 if side A is greater, > 0 if side B is greater, or 0 if equal
*/
int ipfs_cid_compare(const struct Cid* a, const struct Cid* b) {
if (a == NULL && b == NULL)
return 0;
if (a != NULL && b == NULL)
return -1;
if (a == NULL && b != NULL)
return 1;
if (a->version != b->version) {
return b->version - a->version;
}
if (a->codec != b->codec) {
return ((int)b->codec - (int)a->codec);
}
if (a->hash_length != b->hash_length) {
return b->hash_length - a->hash_length;
}
for(size_t i = 0; i < a->hash_length; i++) {
if (a->hash[i] != b->hash[i]) {
return ((int)b->hash[i] - (int)a->hash[i]);
}
}
return 0;
}

185
cid/set.c
View file

@ -1,185 +0,0 @@
#include <stdlib.h>
#include <string.h>
#include "ipfs/cid/cid.h"
#include "ipfs/util/errs.h"
struct CidSet *ipfs_cid_set_new ()
{
return calloc(1, sizeof(struct CidSet));
}
void ipfs_cid_set_destroy (struct CidSet **set)
{
struct CidSet *prev;
if (set) {
while (*set) {
prev = *set;
*set = (*set)->next;
if (prev->cid) {
free (prev->cid);
}
free (prev);
}
}
}
int ipfs_cid_set_add (struct CidSet *set, struct Cid *cid, int visit)
{
if (!set || !cid) {
return ErrInvalidParam;
}
if (!set->cid) { // First item.
set->cid = malloc(sizeof (struct Cid));
if (!set->cid) {
return ErrAllocFailed;
}
memcpy(set->cid, cid, sizeof (struct Cid));
set->cid->hash = calloc(1, cid->hash_length);
if (!set->cid->hash) {
free (set->cid);
return ErrAllocFailed;
}
memcpy(set->cid->hash, cid->hash, cid->hash_length);
return 0;
}
for (;;) {
if ((set->cid->hash_length == cid->hash_length) &&
(memcmp(set->cid->hash, cid->hash, cid->hash_length)==0)) {
// Already added.
if (!visit) {
// update with new cid.
free(set->cid->hash);
memcpy(set->cid, cid, sizeof (struct Cid));
set->cid->hash = calloc(1, cid->hash_length);
if (!set->cid->hash) {
return ErrAllocFailed;
}
memcpy(set->cid->hash, cid->hash, cid->hash_length);
}
return 0;
}
if (!set->next) {
set->next = ipfs_cid_set_new();
if (!set->next) {
return ErrAllocFailed;
}
set = set->next;
set->cid = malloc(sizeof (struct Cid));
if (!set->cid) {
return ErrAllocFailed;
}
memcpy(set->cid, cid, sizeof (struct Cid));
set->cid->hash = calloc(1, cid->hash_length);
if (!set->cid->hash) {
return ErrAllocFailed;
}
memcpy(set->cid->hash, cid->hash, cid->hash_length);
return 0;
}
set = set->next;
}
//this should never get hit
return 0;
}
int ipfs_cid_set_has (struct CidSet *set, struct Cid *cid)
{
if (!set || !cid || !set->cid) {
return 0;
}
for (;;) {
if ((set->cid->hash_length == cid->hash_length) &&
(memcmp(set->cid->hash, cid->hash, cid->hash_length)==0)) {
return 1; // has
}
if (!set->next) {
return 0; // end without found.
}
set = set->next;
}
return 0;
}
int ipfs_cid_set_remove (struct CidSet *set, struct Cid *cid)
{
struct CidSet *prev = set;
if (!set || !cid || !set->cid) {
return 0;
}
for (;;) {
if ((set->cid->hash_length == cid->hash_length) &&
(memcmp(set->cid->hash, cid->hash, cid->hash_length)==0)) {
free (set->cid);
if (prev == set) { // first item
set = set->next;
if (!set) {
prev->cid = NULL;
return 1;
}
prev->cid = set->cid;
} else {
prev->next = set->next;
}
free (set);
return 1; // removed
}
if (!set->next) {
return 0; // end without found.
}
prev = set;
set = set->next;
}
return 0;
}
int ipfs_cid_set_len (struct CidSet *set)
{
int len;
if (!set || !set->cid) {
return 0;
}
for (len = 0 ; set ; len++, set = set->next);
return len;
}
unsigned char **ipfs_cid_set_keys (struct CidSet *set)
{
int i, len=ipfs_cid_set_len(set);
unsigned char **ret;
ret = calloc(len+1, sizeof(char*));
if (ret) {
for (i = 0 ; i<len ; len++) {
if (set && set->cid && set->cid->hash) {
ret[i] = calloc(1, set->cid->hash_length + 1);
if (ret[i]) {
memcpy(ret[i], set->cid->hash, set->cid->hash_length);
}
}
set = set->next;
}
}
return ret;
}
int ipfs_cid_set_foreach (struct CidSet *set, int (*func)(struct Cid *))
{
int err = 0;
while (set) {
if (set->cid) {
err = func (set->cid);
if (err) {
return err;
}
}
set = set->next;
}
return err;
}

View file

@ -1,23 +1,5 @@
CC = gcc
CFLAGS = -O0 -I../include -I../c-libp2p/include -I../c-libp2p/c-multiaddr/include -I../c-libp2p/c-protobuf -Wall -std=gnu99
ifdef DEBUG
CFLAGS += -g3
endif
LFLAGS =
DEPS =
OBJS = cli.o
%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
ipfs: $(OBJS)
$(CC) -o $@ $^ $(LFLAGS)
all: $(OBJS)
all:
cd ipfs; make all;
clean:
rm -f *.o
cd ipfs; make clean;
cd ipfs; make clean;

View file

@ -1,59 +0,0 @@
#include <stdlib.h>
#include <string.h>
#include "ipfs/cmd/cli.h"
int cli_is_switch(int argc, char** argv, int index) {
char* to_test = argv[index];
if (to_test[0] == '-') {
if (strcmp(to_test, "-c") == 0 || strcmp(to_test, "--config") == 0) {
return 2;
}
return 1;
}
return 0;
}
int cli_get_verb_index(struct CliArguments* args) {
for(int i = 1; i < args->argc; i++) {
int advance_by_more_than_one = cli_is_switch(args->argc, args->argv, i);
if (advance_by_more_than_one == 0) {
// this is the verb
return i;
} else {
if (advance_by_more_than_one == 2) {
// skip the next one
i++;
}
}
}
return 0;
}
char* cli_get_config_dir(struct CliArguments* args) {
for (int i = 1; i < args->argc; i++) {
if (args->argv[i][0] == '-') {
char* param = args->argv[i];
if ((strcmp(param, "-c") == 0 || strcmp(param, "--config") == 0) && args->argc > i + 1) {
return args->argv[i+1];
}
}
}
return NULL;
}
struct CliArguments* cli_arguments_new(int argc, char** argv) {
struct CliArguments* args = (struct CliArguments*) malloc(sizeof(struct CliArguments));
if (args != NULL) {
args->argc = argc;
args->argv = argv;
args->verb_index = cli_get_verb_index(args);
args->config_dir = cli_get_config_dir(args);
}
return args;
}
void cli_arguments_free(struct CliArguments* args) {
free(args);
}

View file

@ -1,5 +1,5 @@
CC = gcc
CFLAGS = -O0 -I../../include -I../../c-libp2p/include -I../../c-libp2p/c-multiaddr/include -I../../c-libp2p/c-protobuf -Wall
CFLAGS = -O0 -I../../include -I../../../c-libp2p/include
ifdef DEBUG
CFLAGS += -g3
@ -19,4 +19,4 @@ all: $(OBJS)
clean:
rm -f *.o
rm -f ipfs
rm -f ipfs

View file

@ -1,11 +1,10 @@
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include "ipfs/cmd/ipfs/init.h"
#include "ipfs/commands/request.h"
#include "ipfs/commands/command_option.h"
#include "libp2p/os/utils.h"
#include "ipfs/os/utils.h"
#include "ipfs/core/ipfs_node.h"
#include "ipfs/core/builder.h"
#include "ipfs/repo/config/config.h"
@ -35,9 +34,9 @@ int initialize_ipns_keyspace(struct FSRepo* repo) {
return 0;
//TODO: make a new node, then close it
//TODO: setup offline routing on new node
struct IpfsNode* ipfs_node = NULL;
struct Context* ctx = NULL;
struct BuildCfg* bld_cfg = NULL;
struct IpfsNode* ipfs_node;
struct Context* ctx;
struct BuildCfg* bld_cfg;
//TODO: see line 185 of init.go, what does core.BldCfg{Repo: r} do? BldCfg is a structure
retVal = ipfs_core_builder_new_node(ctx, bld_cfg, ipfs_node);
//return namesys_initialize_keyspace(ctx, ipfs_node->DAG, ipfs_node->Namesys, ipfs_node->pinning, ipfs_node->private_key);
@ -61,8 +60,8 @@ int do_init(FILE* out_file, char* repo_root, int empty, int num_bits_for_keypair
if (fs_repo_is_initialized(repo_root))
return 0;
//TODO: If the conf is null, make one
if ( conf->identity->peer == NULL || conf->identity->peer->id == NULL) {
int retVal = ipfs_repo_config_init(conf, num_bits_for_keypair, repo_root, 4001, NULL);
if ( conf->identity->peer_id == NULL) {
int retVal = ipfs_repo_config_init(conf, num_bits_for_keypair, repo_root);
if (retVal == 0)
return 0;
}
@ -89,8 +88,7 @@ int init_run(struct Request* request) {
// TODO: check parameters for logic errors
// TODO: Initialize
struct RepoConfig* conf;
if (ipfs_repo_config_new(&conf) == 0)
return 0;
int retVal = ipfs_repo_config_new(&conf);
// TODO: handle files in request
// do the heavy lifting
int num_bits_for_key_pair = request->cmd.options[0]->default_int_val;

View file

@ -1,5 +1,5 @@
CC = gcc
CFLAGS = -O0 -I../include -I../c-libp2p/include -Wall -std=gnu99
CFLAGS = -O0 -I../include -I../../c-libp2p/include
LFLAGS =
DEPS = ../include/ipfs/commands/argument.h ../include/ipfs/commands/command_option.h \
../include/ipfs/commands/command.h ../include/ipfs/commands/context.h \

View file

@ -1,5 +1,5 @@
CC = gcc
CFLAGS = -O0 -I../../include -I../../c-libp2p/include -Wall
CFLAGS = -O0 -I../../include -I../../../c-libp2p/include
LFLAGS =
DEPS = parse.h
OBJS = parse.o

View file

@ -1,23 +1,5 @@
#include <stdlib.h>
#include "libp2p/utils/logger.h"
#include "ipfs/commands/cli/parse.h"
/***
* Parse command line arguments, and place them in a Command struct
* @param argc number of arguments
* @param argv arguments
* @param inStream an incoming stream (not implemented yet)
* @param cmd the Command struct to allocate
* @param request not sure what this is for yet
* @returns true(1) on success, false(0) otherwise
*/
int cli_parse(int argc, char** argv, FILE* inStream, struct Command** cmd, struct Request* request) {
*cmd = (struct Command*) malloc(sizeof(struct Command));
if (*cmd == NULL) {
libp2p_logger_error("parse", "Unable to allocate memory for the command structure.\n");
return 0;
}
int cli_parse(char** params, FILE* inStream, struct Command* cmd, struct Request* request) {
return 0;
}

View file

@ -1,3 +1,10 @@
//
// command.c
// c-ipfs
//
// Created by John Jones on 10/27/16.
// Copyright © 2016 JMJAtlanta. All rights reserved.
//
#include <stdlib.h>
#include "ipfs/commands/command.h"

View file

@ -1,3 +1,11 @@
//
// option.c
// c-ipfs
//
// Created by John Jones on 10/26/16.
// Copyright © 2016 JMJAtlanta. All rights reserved.
//
#include <stdio.h>
#include <stdlib.h>

View file

@ -1,13 +1,8 @@
CC = gcc
CFLAGS = -O0 -I../include -I../c-libp2p/include -I../c-libp2p/c-multiaddr/include -I../c-libp2p/c-protobuf -Wall -std=gnu99
ifdef DEBUG
CFLAGS += -g3
endif
CFLAGS = -O0 -I../include -I../../c-libp2p/include
LFLAGS =
DEPS = builder.h ipfs_node.h
OBJS = builder.o daemon.o null.o ping.o bootstrap.o ipfs_node.o api.o client_api.o http_request.o swarm.o
OBJS = builder.o
%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)

View file

@ -1,772 +0,0 @@
/**
* Methods for lightweight/specific HTTP for API communication.
*/
#define _GNU_SOURCE
#define __USE_GNU
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/uio.h>
#include <fcntl.h>
#include "libp2p/net/p2pnet.h"
#include "libp2p/os/memstream.h"
#include "libp2p/utils/logger.h"
#include "libp2p/utils/urlencode.h"
#include "ipfs/core/api.h"
#include "ipfs/importer/exporter.h"
#include "ipfs/core/http_request.h"
//pthread_mutex_t conns_lock;
//int conns_count;
struct ApiContext api_list;
/**
* Write two strings on one write.
* @param fd file descriptor to write.
* @param str1 first string to write.
* @param str2 second string to write.
*/
size_t write_dual(int fd, char *str1, char *str2)
{
struct iovec iov[2];
iov[0].iov_base = str1;
iov[0].iov_len = strlen(str1);
iov[1].iov_base = str2;
iov[1].iov_len = strlen(str2);
return writev(fd, iov, 2);
}
int find_chunk(char *buf, const size_t buf_size, size_t *pos, size_t *size)
{
char *p = NULL;
*size = strtol(buf, &p, 16);
if (!p || p < buf || p > (buf + 10)) {
return 0;
}
*pos = (int)(p - buf);
if (p[0] == '\r' && p[1] == '\n') {
*pos += 2;
return 1;
}
return 0;
}
int read_chunked(int fd, struct s_request *req, char *already, size_t already_size)
{
char buf[MAX_READ], *p;
size_t pos, nsize, buf_size = 0, r;
if (already_size > 0) {
if (already_size <= sizeof(buf)) {
memcpy(buf, already, already_size);
buf_size += already_size;
already_size = 0;
} else {
memcpy(buf, already, sizeof(buf));
already += sizeof(buf);
buf_size += sizeof(buf);
already_size -= sizeof(buf);
}
}
while(buf_size) {
if (!find_chunk(buf, buf_size, &pos, &nsize)) {
libp2p_logger_error("api", "fail find_chunk.\n");
libp2p_logger_error("api", "nsize = %d.\n", nsize);
return 0;
}
if (nsize == 0) {
break;
}
p = realloc(req->buf, req->size + nsize);
if (!p) {
libp2p_logger_error("api", "fail realloc.\n");
return 0;
}
req->buf = p;
req->size += nsize;
CPCHUNK:
r = nsize;
buf_size -= pos;
if (r > buf_size) {
r = buf_size;
}
memcpy(req->buf + req->body + req->body_size, buf + pos, r);
req->body_size += r;
nsize -= r;
buf_size -= r;
if (buf_size > 0) {
memmove(buf, buf + pos + r, buf_size);
}
pos = 0;
if (already_size > 0) {
r = sizeof(buf) - buf_size;
if (already_size <= r) {
memcpy(buf, already, already_size);
buf_size += already_size;
already_size = 0;
} else {
memcpy(buf, already, r);
already += r;
buf_size += r;
already_size -= r;
}
}
if (socket_read_select4(fd, 5) > 0) {
r = sizeof(buf) - buf_size;
r = read(fd, buf+buf_size, r);
buf_size += r;
if (r == 0 && nsize == 0) {
break;
}
if (r <= 0) {
libp2p_logger_error("api", "read fail.\n");
return 0;
}
}
if (nsize > 0)
goto CPCHUNK; // still have data to transfer on current chunk.
if (memcmp (buf, "\r\n", 2)!=0) {
libp2p_logger_error("api", "fail CRLF.\n");
return 0;
}
}
return 1;
}
int read_all(int fd, struct s_request *req, char *already, size_t alread_size)
{
char buf[MAX_READ], *p;
size_t size = 0;
if (alread_size > 0) {
p = realloc(req->buf, req->size + alread_size);
if (!p) {
return 0;
}
req->buf = p;
req->size += alread_size;
memcpy(req->buf + req->body + req->body_size, already, alread_size);
req->body_size += alread_size;
}
for(;;) {
if (socket_read_select4(fd, 5) <= 0) {
break;
}
size = read(fd, buf, sizeof buf);
if (size <= 0) {
break;
}
p = realloc(req->buf, req->size + size);
if (!p) {
return 0;
}
req->buf = p;
req->size += size;
memcpy(req->buf + req->body + req->body_size, buf, size);
req->body_size += size;
}
return 1;
}
/**
* Find a token in a string array.
* @param string array and token string.
* @returns the pointer after where the token was found or NULL if it fails.
*/
char *str_tok(char *str, char *tok)
{
char *p = strstr(str, tok);
if (p) {
p += strlen(tok);
while(*p == ' ') p++;
}
return p;
}
/**
* Find a token in a binary array.
* @param array, size of array, token and size of token.
* @returns the pointer after where the token was found or NULL if it fails.
*/
char *bin_tok(char *bin, size_t limit, char *tok, size_t tok_size)
{
char *p = memmem(bin, limit, tok, tok_size);
if (p) {
p += tok_size;
}
return p;
}
/**
* Check if header contain a especific value.
* @param request structure, header name and value to check.
* @returns the pointer where the value was found or NULL if it fails.
*/
char *header_value_cmp(struct s_request *req, char *header, char *value)
{
char *p = str_tok(req->buf + req->header, header);
if (p) {
if (strstart(p, value)) {
return p;
}
}
return NULL;
}
/**
* Lookup for boundary at buffer string.
* @param body buffer string, boundary id, filename and content-type string.
* @returns the pointer where the multipart start.
*/
char *boundary_find(char *str, char *boundary, char **filename, char **contenttype)
{
char *p = str_tok(str, "--");
while (p) {
if (strstart(p, boundary)) {
// skip to the beginning, ignoring the header for now, if there is.
// TODO: return filename and content-type
p = strstr(p, "\r\n\r\n");
if (p) {
return p + 4; // ignore 4 bytes CRLF 2x
}
break;
}
p = str_tok(str, "--");
}
return NULL;
}
/**
* Return the size of boundary.
* @param boundary buffer, boundary id.
* @returns the size of boundary or 0 if fails.
*/
size_t boundary_size(char *str, char *boundary, size_t limit)
{
char *p = bin_tok(str, limit, "\r\n--", 4);
while (p) {
if (strstart(p, boundary)) {
if (cstrstart(p + strlen(boundary), "--\r\n")) {
p -= 4;
return (size_t)(p - str);
}
}
p = bin_tok(p, limit, "\r\n--", 4);
}
return 0;
}
struct ApiConnectionParam {
int index;
struct IpfsNode* this_node;
};
/***
* Take an s_request and turn it into an HttpRequest
* @param req the incoming s_request
* @returns the resultant HttpRequest or NULL on error
*/
struct HttpRequest* api_build_http_request(struct s_request* req) {
struct HttpRequest* request = ipfs_core_http_request_new();
if (request != NULL) {
char *segs = malloc (strlen(req->buf + req->request) + 1);
if (segs) {
strcpy(segs, req->buf + req->request);
request->command = segs;
segs = strchr(segs, '/');
if (segs) {
*segs++ = '\0';
request->sub_command = segs; // sub_command can contain another level as filters/add
}
if (req->query) {
segs = libp2p_utils_url_decode(req->buf + req->query);
if (segs) {
while (segs) {
char *value, *name = segs;
segs = strchr(segs, '&');
if (segs) { // calc next to split before search for = on another parameter.
*segs++ = '\0';
}
value = strchr(name, '=');
if (value) {
*value++ = '\0';
}
if (value && (strcmp(name, "arg")==0)) {
libp2p_utils_vector_add(request->arguments, strdup(value));
} else {
struct HttpParam *hp = ipfs_core_http_param_new();
if (hp) {
hp->name = strdup(name);
hp->value = strdup(value); // maybe null ?
libp2p_utils_vector_add(request->params, hp);
}
}
}
free(segs);
}
}
}
}
return request;
}
/**
* Write bytes into chunks.
* @param socket, buffer array, length
* @returns 1 when success or 0 if it fails.
*/
int api_send_resp_chunks(int fd, void *buf, size_t size)
{
char head[20];
size_t s;
int l;
struct iovec iov[3];
// will be reused in each write, so defined only once.
iov[2].iov_base = "\r\n";
iov[2].iov_len = 2;
while (size > 0) {
s = size > MAX_CHUNK ? MAX_CHUNK : size; // write only MAX_CHUNK at once
l = snprintf(head, sizeof head, "%x\r\n", (unsigned int)s);
if (l <= 0)
return 0; // fail at snprintf
iov[0].iov_base = head;
iov[0].iov_len = l; // head length.
iov[1].iov_base = buf;
iov[1].iov_len = s;
buf += s;
size -= s;
if (size == 0) { // last chunk
iov[2].iov_base = "\r\n0\r\n\r\n";
iov[2].iov_len = 7;
}
libp2p_logger_debug("api", "writing chunk block of %d bytes\n", s);
if (writev(fd, iov, 3) == -1)
return 0; // fail writing.
}
return 1;
}
/**
* Pthread to take care of each client connection.
* @param ptr an ApiConnectionParam
* @returns nothing
*/
void *api_connection_thread (void *ptr)
{
int timeout, s, r;
struct ApiConnectionParam* params = (struct ApiConnectionParam*)ptr;
char resp[MAX_READ+1], buf[MAX_READ+1], *p, *body;
char client[INET_ADDRSTRLEN];
struct s_request req;
int (*read_func)(int, struct s_request*, char*, size_t) = read_all;
req.buf = NULL; // sanity.
buf[MAX_READ] = '\0';
s = params->this_node->api_context->conns[params->index]->socket;
timeout = params->this_node->api_context->timeout;
if (socket_read_select4(s, timeout) <= 0) {
libp2p_logger_error("api", "Client connection timeout.\n");
goto quit;
}
r = read(s, buf, sizeof buf);
if (r <= 0) {
// this is a common occurrence, so moved from error to debug
libp2p_logger_debug("api", "Read from client fail.\n");
goto quit;
}
buf[r] = '\0';
p = strstr(buf, "\r\n\r\n");
if (p) {
body = p + 4;
req.size = p - buf + 1;
req.buf = malloc(req.size);
if (!req.buf) {
// memory allocation fail.
libp2p_logger_error("api", "malloc fail.\n");
write_cstr (s, HTTP_500);
goto quit;
}
memcpy(req.buf, buf, req.size - 1);
req.buf[req.size-1] = '\0';
req.method = 0;
p = strchr(req.buf + req.method, ' ');
if (!p) {
libp2p_logger_error("api", "fail looking for space on method '%s'.\n", req.buf + req.method);
write_cstr (s, HTTP_400);
goto quit;
}
*p++ = '\0'; // End of method.
req.path = p - req.buf;
if (strchr(p, '?')) {
p = strchr(p, '?');
*p++ = '\0';
req.query = p - req.buf;
} else {
req.query = 0;
}
p = strchr(p, ' ');
if (!p) {
libp2p_logger_error("api", "fail looking for space on path '%s'.\n", req.buf + req.path);
write_cstr (s, HTTP_400);
goto quit;
}
*p++ = '\0'; // End of path.
req.http_ver = p - req.buf;
p = strchr(req.buf + req.http_ver, '\r');
if (!p) {
libp2p_logger_error("api", "fail looking for CR on http_ver '%s'.\n", req.buf + req.http_ver);
write_cstr (s, HTTP_400);
goto quit;
}
*p++ = '\0'; // End of http version.
while (*p == '\r' || *p == '\n') p++;
req.header = p - req.buf;
req.body = req.size;
req.body_size = 0;
if (header_value_cmp(&req, "Transfer-Encoding:", "chunked")) {
read_func = read_chunked;
}
if (!read_func(s, &req, body, r - (body - buf))) {
libp2p_logger_error("api", "fail read_func.\n");
write_cstr (s, HTTP_500);
goto quit;
}
if (strncmp(req.buf + req.method, "GET", 3)==0) {
if (strcmp (req.buf + req.path, "/")==0 ||
strcmp (req.buf + req.path, "/webui")==0 ||
strcmp (req.buf + req.path, "/webui/")==0) {
char *redir;
size_t size = sizeof(HTTP_301) + (sizeof(WEBUI_ADDR)*2);
redir = malloc(size);
if (redir) {
snprintf(redir, size, HTTP_301, WEBUI_ADDR, WEBUI_ADDR);
redir[size-1] = '\0'; // just in case
write_dual (s, req.buf + req.http_ver, strchr (redir, ' '));
free (redir);
} else {
write_cstr (s, HTTP_500);
}
} else if (!cstrstart(req.buf + req.path, API_V0_START)) {
// TODO: handle download file here.
// move out of the if to do further processing
}
// end of GET
} else if (strncmp(req.buf + req.method, "POST", 4)==0) {
// TODO: Handle gzip/json POST requests.
p = header_value_cmp(&req, "Content-Type:", "multipart/form-data;");
if (p) {
p = str_tok(p, "boundary=");
if (p) {
char *boundary, *l;
int len;
if (*p == '"') {
p++;
l = strchr(p, '"');
} else {
l = p;
while (*l != '\r' && *l != '\0') l++;
}
len = l - p;
boundary = malloc (len+1);
if (boundary) {
memcpy(boundary, p, len);
boundary[len] = '\0';
p = boundary_find(req.buf + req.body, boundary, NULL, NULL);
if (p) {
req.boundary_size = boundary_size(p, boundary, req.size - (p - buf));
if (req.boundary_size > 0) {
req.boundary = p - req.buf;
}
}
free (boundary);
}
}
}
if (req.boundary > 0) {
libp2p_logger_error("api", "boundary index = %d, size = %d\n", req.boundary, req.boundary_size);
}
libp2p_logger_debug("api", "method = '%s'\n"
"path = '%s'\n"
"http_ver = '%s'\n"
"header {\n%s\n}\n"
"body_size = %d\n",
req.buf+req.method, req.buf+req.path, req.buf+req.http_ver,
req.buf+req.header, req.body_size);
// end of POST
} else {
// Unexpected???
libp2p_logger_error("api", "fail unexpected '%s'.\n", req.buf + req.method);
write_cstr (s, HTTP_500);
}
if (cstrstart(req.buf + req.path, API_V0_START)) {
req.request = req.path + sizeof(API_V0_START) - 1;
// now do something with the request we have built
struct HttpRequest* http_request = api_build_http_request(&req);
if (http_request != NULL) {
struct HttpResponse* http_response = NULL;
if (!ipfs_core_http_request_process(params->this_node, http_request, &http_response)) {
libp2p_logger_error("api", "ipfs_core_http_request_process returned false.\n");
// 404
write_str(s, HTTP_404);
} else {
snprintf(resp, MAX_READ+1, "%s 200 OK\r\n" \
"Content-Type: %s\r\n"
"Server: c-ipfs/0.0.0-dev\r\n"
"X-Chunked-Output: 1\r\n"
"Connection: close\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
,req.buf + req.http_ver, http_response->content_type);
write_str (s, resp);
api_send_resp_chunks(s, http_response->bytes, http_response->bytes_size);
libp2p_logger_debug("api", "resp = {\n%s\n}\n", resp);
}
ipfs_core_http_request_free(http_request);
ipfs_core_http_response_free(http_response);
} else {
// uh oh... something went wrong converting to the HttpRequest struct
libp2p_logger_error("api", "Unable to build HttpRequest struct.\n");
}
}
} else {
libp2p_logger_error("api", "fail looking for body.\n");
write_cstr (s, HTTP_400);
}
quit:
if (req.buf)
free(req.buf);
if (inet_ntop(AF_INET, &( params->this_node->api_context->conns[params->index]->ipv4), client, INET_ADDRSTRLEN) == NULL)
strcpy(client, "UNKNOW");
libp2p_logger_debug("api", "Closing client connection %s:%d (%d).\n", client, params->this_node->api_context->conns[params->index]->port, params->index+1);
pthread_mutex_lock(&params->this_node->api_context->conns_lock);
close(s);
free ( params->this_node->api_context->conns[params->index]);
params->this_node->api_context->conns[params->index] = NULL;
params->this_node->api_context->conns_count--;
pthread_mutex_unlock(&params->this_node->api_context->conns_lock);
free(params);
return NULL;
}
/**
* Close all connections stopping respectives pthreads and free allocated memory.
*/
void api_connections_cleanup (struct IpfsNode* local_node)
{
int i;
pthread_mutex_lock(&local_node->api_context->conns_lock);
if (local_node->api_context->conns_count > 0 && local_node->api_context->conns) {
for (i = 0 ; i < local_node->api_context->max_conns ; i++) {
if (local_node->api_context->conns[i]->pthread) {
pthread_cancel (local_node->api_context->conns[i]->pthread);
close (local_node->api_context->conns[i]->socket);
free (local_node->api_context->conns[i]);
local_node->api_context->conns[i] = NULL;
}
}
local_node->api_context->conns_count = 0;
}
if (local_node->api_context->conns) {
free (local_node->api_context->conns);
local_node->api_context->conns = NULL;
}
pthread_mutex_unlock(&local_node->api_context->conns_lock);
}
/**
* Pthread to keep in background dealing with client connections.
* @param ptr is not used.
* @returns nothing
*/
void *api_listen_thread (void *ptr)
{
int s;
INT_TYPE i;
uint32_t ipv4;
uint16_t port;
char client[INET_ADDRSTRLEN];
struct IpfsNode* local_node = (struct IpfsNode*)ptr;
local_node->api_context->conns_count = 0;
for (;;) {
s = socket_accept4(local_node->api_context->socket, &ipv4, &port);
if (s <= 0) {
break;
}
if (local_node->api_context->conns_count >= local_node->api_context->max_conns) { // limit reached.
libp2p_logger_error("api", "Limit of connections reached (%d).\n", local_node->api_context->max_conns);
close (s);
continue;
}
pthread_mutex_lock(&local_node->api_context->conns_lock);
for (i = 0 ; i < local_node->api_context->max_conns && local_node->api_context->conns[i] ; i++);
local_node->api_context->conns[i] = malloc (sizeof (struct s_conns));
if (!local_node->api_context->conns[i]) {
libp2p_logger_error("api", "Fail to allocate memory to accept connection.\n");
pthread_mutex_unlock(&local_node->api_context->conns_lock);
close (s);
continue;
}
if (inet_ntop(AF_INET, &ipv4, client, INET_ADDRSTRLEN) == NULL)
strcpy(client, "UNKNOW");
local_node->api_context->conns[i]->socket = s;
local_node->api_context->conns[i]->ipv4 = ipv4;
local_node->api_context->conns[i]->port = port;
// create a struct, which the thread is responsible to destroy
struct ApiConnectionParam* connection_param = (struct ApiConnectionParam*) malloc(sizeof(struct ApiConnectionParam));
if (connection_param == NULL) {
libp2p_logger_error("api", "api_listen_thread: Unable to allocate memory.\n");
pthread_mutex_unlock(&local_node->api_context->conns_lock);
close (s);
continue;
}
connection_param->index = i;
connection_param->this_node = local_node;
if (pthread_create(&(local_node->api_context->conns[i]->pthread), NULL, api_connection_thread, (void*)connection_param)) {
libp2p_logger_error("api", "Create pthread fail.\n");
free (local_node->api_context->conns[i]);
local_node->api_context->conns[i] = NULL;
local_node->api_context->conns_count--;
close(s);
} else {
local_node->api_context->conns_count++;
}
libp2p_logger_debug("api", "API for %s: Accept connection %s:%d (%d/%d), pthread %d.\n", local_node->identity->peer->id, client, port, local_node->api_context->conns_count, local_node->api_context->max_conns, i+1);
pthread_mutex_unlock(&local_node->api_context->conns_lock);
}
api_connections_cleanup (local_node);
return NULL;
}
struct ApiContext* api_context_new() {
struct ApiContext* context = (struct ApiContext*) malloc(sizeof(struct ApiContext));
if (context != NULL) {
context->conns = NULL;
context->conns_count = 0;
context->ipv4 = 0;
context->max_conns = 0;
context->port = 0;
context->socket = 0;
context->timeout = 0;
pthread_mutex_init(&context->conns_lock, NULL);
}
return context;
}
/**
* Start API interface daemon.
* @param local_node the context
* @param max_conns.
* @param timeout time out of client connection.
* @returns 0 when failure or 1 if success.
*/
int api_start (struct IpfsNode* local_node, int max_conns, int timeout)
{
int s;
size_t alloc_size = sizeof(void*) * max_conns;
struct MultiAddress* my_address = multiaddress_new_from_string(local_node->repo->config->addresses->api);
char* ip = NULL;
multiaddress_get_ip_address(my_address, &ip);
int port = multiaddress_get_ip_port(my_address);
local_node->api_context = api_context_new();
if (local_node->api_context == NULL) {
multiaddress_free(my_address);
return 0;
}
local_node->api_context->ipv4 = hostname_to_ip(ip); // api is listening only on loopback.
if (ip != NULL)
free(ip);
local_node->api_context->port = port;
if ((s = socket_listen(socket_tcp4(), &(local_node->api_context->ipv4), &(local_node->api_context->port))) <= 0) {
libp2p_logger_error("api", "Failed to init API. port: %d\n", port);
return 0;
}
local_node->api_context->socket = s;
local_node->api_context->max_conns = max_conns;
local_node->api_context->timeout = timeout;
local_node->api_context->conns = malloc (alloc_size);
if (!local_node->api_context->conns) {
close (s);
libp2p_logger_error("api", "Error allocating memory.\n");
return 0;
}
memset(local_node->api_context->conns, 0, alloc_size);
if (pthread_create(&local_node->api_context->api_thread, NULL, api_listen_thread, (void*)local_node)) {
close (s);
free (local_node->api_context->conns);
local_node->api_context->conns = NULL;
local_node->api_context->api_thread = 0;
libp2p_logger_error("api", "Error creating thread for API.\n");
return 0;
}
libp2p_logger_info("api", "API server listening on %d.\n", port);
return 1;
}
/**
* Stop API.
* @returns 0 when failure or 1 if success.
*/
int api_stop (struct IpfsNode *local_node)
{
if (local_node->api_context->api_thread == 0) return 0;
shutdown(local_node->api_context->socket, SHUT_RDWR);
pthread_cancel(local_node->api_context->api_thread);
api_connections_cleanup (local_node);
local_node->api_context->api_thread = 0;
return 1;
}

View file

@ -1,79 +0,0 @@
#include <pthread.h>
#include "libp2p/peer/peer.h"
#include "libp2p/utils/logger.h"
#include "ipfs/routing/routing.h"
#include "ipfs/core/ipfs_node.h"
#include "ipfs/thirdparty/ipfsaddr/ipfs_addr.h"
#include "multiaddr/multiaddr.h"
/***
* Begin to connect to the swarm
*/
/*
void *ipfs_bootstrap_swarm(void* param) {
//TODO:
struct IpfsNode* local_node = (struct IpfsNode*)param;
// read the config file and get the bootstrap peers
for(int i = 0; i < local_node->repo->config->peer_addresses.num_peers; i++) { // loop through the peers
struct IPFSAddr* ipfs_addr = local_node->repo->config->peer_addresses.peers[i];
struct MultiAddress* ma = multiaddress_new_from_string(ipfs_addr->entire_string);
// get the id
char* ptr;
if ( (ptr = strstr(ipfs_addr->entire_string, "/ipfs/")) != NULL) { // look for the peer id
ptr += 6;
if (ptr[0] == 'Q' && ptr[1] == 'm') { // things look good
struct Libp2pPeer* peer = libp2p_peer_new_from_data(ptr, strlen(ptr), ma);
libp2p_peerstore_add_peer(local_node->peerstore, peer);
}
// TODO: attempt to connect to the peer
} // we have a good peer ID
}
return (void*)1;
}
*/
/***
* Announce to the network all of the files that I have in storage
* @param local_node the context
*/
/*
void ipfs_bootstrap_announce_files(struct IpfsNode* local_node) {
struct Datastore* db = local_node->repo->config->datastore;
if (!db->datastore_cursor_open(db))
return;
unsigned char* key = NULL;
int key_size = 0;
enum DatastoreCursorOp op = CURSOR_FIRST;
while (db->datastore_cursor_get(&key, &key_size, NULL, 0, op, db)) {
libp2p_logger_debug("bootstrap", "Announcing a file to the world.\n");
local_node->routing->Provide(local_node->routing, key, key_size);
op = CURSOR_NEXT;
free(key);
}
// close cursor
db->datastore_cursor_close(db);
return;
}
*/
/***
* connect to the swarm
* NOTE: This fills in the IpfsNode->routing struct
*
* @param param the IpfsNode information
* @returns nothing useful
*/
/*
void *ipfs_bootstrap_routing(void* param) {
struct IpfsNode* local_node = (struct IpfsNode*)param;
local_node->routing = ipfs_routing_new_online(local_node, &local_node->identity->private_key, NULL);
local_node->routing->Bootstrap(local_node->routing);
ipfs_bootstrap_announce_files(local_node);
return (void*)2;
}
*/

View file

@ -1,4 +1,11 @@
#include <pthread.h>
//
// builder.c
// c-ipfs
//
// Created by John Jones on 10/27/16.
// Copyright © 2016 JMJAtlanta. All rights reserved.
//
#include "ipfs/core/builder.h"
int ipfs_core_builder_new_node(struct Context* context, struct BuildCfg* build_cfg, struct IpfsNode* buildConfig) {

View file

@ -1,60 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <pthread.h>
#include "ipfs/core/client_api.h"
#include "multiaddr/multiaddr.h"
/**
* Determine if the API is running (by attempting to connect to the port)
* @param local_node the context
* @returns true(1) on success, false(0) otherwise
*/
int api_running(struct IpfsNode* local_node) {
struct MultiAddress* my_multiaddress = multiaddress_new_from_string(local_node->repo->config->addresses->api);
char* ip = NULL;
int portno = 0;
if (my_multiaddress == NULL) {
return 0;
}
portno = multiaddress_get_ip_port(my_multiaddress);
multiaddress_get_ip_address(my_multiaddress, &ip);
multiaddress_free(my_multiaddress);
if (ip == NULL)
return 0;
int sockfd;
struct sockaddr_in serv_addr;
struct hostent *server;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
free(ip);
return 0;
}
server = gethostbyname(ip);
free(ip);
if (server == NULL) {
return 0;
}
memset(&serv_addr, 0, sizeof(struct sockaddr_in));
serv_addr.sin_family = AF_INET;
memmove(&serv_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length);
serv_addr.sin_port = htons(portno);
int retVal = connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr));
close(sockfd);
return retVal >= 0;
}

View file

@ -1,80 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <pthread.h>
#include "libp2p/net/p2pnet.h"
#include "libp2p/peer/peerstore.h"
#include "ipfs/core/daemon.h"
#include "ipfs/core/null.h" // for ipfs_null_shutdown
#include "ipfs/core/ipfs_node.h"
#include "ipfs/core/bootstrap.h"
#include "ipfs/repo/fsrepo/fs_repo.h"
#include "ipfs/repo/init.h"
#include "libp2p/utils/logger.h"
int ipfs_daemon_start(char* repo_path) {
int count_pths = 0, retVal = 0;
pthread_t work_pths[MAX];
struct IpfsNodeListenParams listen_param;
struct MultiAddress* ma = NULL;
libp2p_logger_info("daemon", "Initializing daemon for %s...\n", repo_path);
struct IpfsNode* local_node = NULL;
if (!ipfs_node_online_new(repo_path, &local_node))
goto exit;
// Set null router param
ma = multiaddress_new_from_string(local_node->repo->config->addresses->swarm_head->item);
listen_param.port = multiaddress_get_ip_port(ma);
listen_param.ipv4 = 0; // ip 0.0.0.0, all interfaces
listen_param.local_node = local_node;
// Create pthread for swarm listener.
if (pthread_create(&work_pths[count_pths++], NULL, local_node->routing->Listen, &listen_param)) {
libp2p_logger_error("daemon", "Error creating thread for ipfs null listen\n");
goto exit;
}
local_node->routing->Bootstrap(local_node->routing);
libp2p_logger_info("daemon", "Daemon for %s is ready on port %d\n", listen_param.local_node->identity->peer->id, listen_param.port);
// Wait for pthreads to finish.
while (count_pths) {
if (pthread_join(work_pths[--count_pths], NULL)) {
libp2p_logger_error("daemon", "Error joining thread\n");
goto exit;
}
}
retVal = 1;
exit:
libp2p_logger_debug("daemon", "Cleaning up daemon processes for %s\n", repo_path);
// clean up
if (ma != NULL)
multiaddress_free(ma);
if (local_node != NULL) {
ipfs_node_free(local_node);
}
return retVal;
}
int ipfs_daemon_stop() {
return ipfs_null_shutdown();
}
int ipfs_daemon (int argc, char **argv)
{
char* repo_path = NULL;
libp2p_logger_add_class("daemon");
if (!ipfs_repo_get_directory(argc, argv, &repo_path)) {
libp2p_logger_error("daemon", "Unable to open repo: %s\n", repo_path);
return 0;
}
return ipfs_daemon_start(repo_path);
}

View file

@ -1,628 +0,0 @@
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include "libp2p/os/memstream.h"
#include "libp2p/utils/vector.h"
#include "libp2p/utils/logger.h"
#include "ipfs/cid/cid.h"
#include "ipfs/core/http_request.h"
#include "ipfs/importer/exporter.h"
#include "ipfs/namesys/resolver.h"
#include "ipfs/namesys/publisher.h"
#include "ipfs/routing/routing.h"
/**
* Handles HttpRequest and HttpParam
*/
/***
* Build a new HttpRequest
* @returns the newly allocated HttpRequest struct
*/
struct HttpRequest* ipfs_core_http_request_new() {
struct HttpRequest* result = (struct HttpRequest*) malloc(sizeof(struct HttpRequest));
if (result != NULL) {
result->command = NULL;
result->sub_command = NULL;
result->params = libp2p_utils_vector_new(1);
result->arguments = libp2p_utils_vector_new(1);
}
return result;
}
/***
* Clean up resources of a HttpRequest struct
* @param request the struct to destroy
*/
void ipfs_core_http_request_free(struct HttpRequest* request) {
if (request != NULL) {
//if (request->command != NULL)
// free(request->command);
//if (request->sub_command != NULL)
// free(request->sub_command);
if (request->params != NULL) {
for(int i = 0; i < request->params->total; i++) {
struct HttpParam* curr_param = (struct HttpParam*) libp2p_utils_vector_get(request->params, i);
ipfs_core_http_param_free(curr_param);
}
libp2p_utils_vector_free(request->params);
}
if (request->arguments != NULL) {
//for(int i = 0; i < request->arguments->total; i++) {
// free((char*)libp2p_utils_vector_get(request->arguments, i));
//}
libp2p_utils_vector_free(request->arguments);
}
free(request);
}
}
struct HttpResponse* ipfs_core_http_response_new() {
struct HttpResponse* response = (struct HttpResponse*) malloc(sizeof(struct HttpResponse));
if (response != NULL) {
response->content_type = NULL;
response->bytes = NULL;
response->bytes_size = 0;
}
return response;
}
void ipfs_core_http_response_free(struct HttpResponse* response) {
if (response != NULL) {
// NOTE: content_type should not be dynamically allocated
if (response->bytes != NULL)
free(response->bytes);
free(response);
}
}
/***
* Build a new HttpParam
* @returns a newly allocated HttpParam struct
*/
struct HttpParam* ipfs_core_http_param_new() {
struct HttpParam* param = (struct HttpParam*) malloc(sizeof(struct HttpParam));
if (param != NULL) {
param->name = NULL;
param->value = NULL;
}
return param;
}
/***
* Clean up resources allocated by a HttpParam struct
* @param param the struct to destroy
*/
void ipfs_core_http_param_free(struct HttpParam* param) {
if (param != NULL) {
if (param->name != NULL)
free(param->name);
if (param->value != NULL)
free(param->value);
free(param);
}
}
/***
* Handle processing of the "name" command
* @param local_node the context
* @param request the incoming request
* @param response the response
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_core_http_process_name(struct IpfsNode* local_node, struct HttpRequest* request, struct HttpResponse** response) {
int retVal = 0;
char* path = NULL;
char* result = NULL;
if (request->arguments == NULL || request->arguments->total == 0) {
libp2p_logger_error("http_request", "process_name: empty arguments\n");
return 0;
}
path = (char*) libp2p_utils_vector_get(request->arguments, 0);
if (strcmp(request->sub_command, "resolve") == 0) {
retVal = ipfs_namesys_resolver_resolve(local_node, path, 1, &result);
if (retVal) {
*response = ipfs_core_http_response_new();
struct HttpResponse* res = *response;
res->content_type = "application/json";
res->bytes = (uint8_t*) malloc(strlen(local_node->identity->peer->id) + strlen(path) + 30);
if (res->bytes == NULL) {
free(result);
return 0;
}
sprintf((char*)res->bytes, "{ \"Path\": \"%s\" }", result);
res->bytes_size = strlen((char*)res->bytes);
}
free(result);
} else if (strcmp(request->sub_command, "publish") == 0) {
retVal = ipfs_namesys_publisher_publish(local_node, path);
if (retVal) {
*response = ipfs_core_http_response_new();
struct HttpResponse* res = *response;
res->content_type = "application/json";
res->bytes = (uint8_t*) malloc(strlen(local_node->identity->peer->id) + strlen(path) + 30);
if (res->bytes == NULL)
return 0;
sprintf((char*)res->bytes, "{ \"Name\": \"%s\"\n \"Value\": \"%s\" }", local_node->identity->peer->id, path);
res->bytes_size = strlen((char*)res->bytes);
}
}
return retVal;
}
/***
* Handle processing of the "object" command
* @param local_node the context
* @param request the incoming request
* @param response the response
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_core_http_process_object(struct IpfsNode* local_node, struct HttpRequest* request, struct HttpResponse** response) {
int retVal = 0;
if (strcmp(request->sub_command, "get") == 0) {
// do an object_get
if (request->arguments->total == 1) {
char* hash = (char*)libp2p_utils_vector_get(request->arguments, 0);
struct Cid* cid = NULL;
if (!ipfs_cid_decode_hash_from_base58((unsigned char*)hash, strlen(hash), &cid)) {
ipfs_cid_free(cid);
cid = NULL;
return 0;
}
*response = ipfs_core_http_response_new();
struct HttpResponse* res = *response;
res->content_type = "application/json";
FILE* response_file = open_memstream((char**)&res->bytes, &res->bytes_size);
retVal = ipfs_exporter_object_cat_to_file(local_node, cid->hash, cid->hash_length, response_file);
ipfs_cid_free(cid);
fclose(response_file);
}
}
return retVal;
}
int ipfs_core_http_process_dht_provide(struct IpfsNode* local_node, struct HttpRequest* request, struct HttpResponse** response) {
int failedCount = 0;
for (int i = 0; i < request->arguments->total; i++) {
char* hash = (char*)libp2p_utils_vector_get(request->arguments, i);
struct Cid* cid;
if (!ipfs_cid_decode_hash_from_base58((unsigned char*)hash, strlen(hash), &cid)) {
ipfs_cid_free(cid);
cid = NULL;
failedCount++;
continue;
}
if (!local_node->routing->Provide(local_node->routing, cid->hash, cid->hash_length)) {
ipfs_cid_free(cid);
cid = NULL;
failedCount++;
continue;
}
ipfs_cid_free(cid);
}
*response = ipfs_core_http_response_new();
struct HttpResponse* res = *response;
res->content_type = "application/json";
res->bytes = (uint8_t*) malloc(1024);
if (res->bytes == NULL) {
res->bytes_size = 0;
} else {
if (!failedCount) {
// complete success
// TODO: do the right thing
snprintf((char*)res->bytes, 1024, "{\n\t\"ID\": \"<string>\"\n" \
"\t\"Type\": \"<int>\"\n"
"\t\"Responses\": [\n"
"\t\t{\n"
"\t\t\t\"ID\": \"<string>\"\n"
"\t\t\t\"Addrs\": [\n"
"\t\t\t\t\"<object>\"\n"
"\t\t\t]\n"
"\t\t}\n"
"\t]\n"
"\t\"Extra\": \"<string>\"\n"
"}\n"
);
} else {
// at least some failed
// TODO: do the right thing
snprintf((char*)res->bytes, 1024, "{\n\t\"ID\": \"<string>\",\n" \
"\t\"Type\": \"<int>\",\n"
"\t\"Responses\": [\n"
"\t\t{\n"
"\t\t\t\"ID\": \"<string>\",\n"
"\t\t\t\"Addrs\": [\n"
"\t\t\t\t\"<object>\"\n"
"\t\t\t]\n"
"\t\t}\n"
"\t],\n"
"\t\"Extra\": \"<string>\"\n"
"}\n"
);
}
res->bytes_size = strlen((char*)res->bytes);
}
return failedCount < request->arguments->total;
}
int ipfs_core_http_process_dht_get(struct IpfsNode* local_node, struct HttpRequest* request, struct HttpResponse** response) {
int failedCount = 0;
// for now, we can only handle 1 argument at a time
if (request->arguments != NULL && request->arguments->total != 1)
return 0;
*response = ipfs_core_http_response_new();
struct HttpResponse* res = *response;
res->content_type = "application/octet-stream";
for (int i = 0; i < request->arguments->total; i++) {
char* hash = (char*)libp2p_utils_vector_get(request->arguments, i);
struct Cid* cid;
if (!ipfs_cid_decode_hash_from_base58((unsigned char*)hash, strlen(hash), &cid)) {
ipfs_cid_free(cid);
cid = NULL;
failedCount++;
continue;
}
if (!local_node->routing->GetValue(local_node->routing, cid->hash, cid->hash_length, (void**)&res->bytes, &res->bytes_size)) {
ipfs_cid_free(cid);
cid = NULL;
failedCount++;
continue;
}
ipfs_cid_free(cid);
//TODO: we need to handle multiple arguments
}
return failedCount < request->arguments->total;
}
/***
* process dht commands
* @param local_node the context
* @param request the request
* @param response where to put the results
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_core_http_process_dht(struct IpfsNode* local_node, struct HttpRequest* request, struct HttpResponse** response) {
int retVal = 0;
if (strcmp(request->sub_command, "provide") == 0) {
// do a dht provide
retVal = ipfs_core_http_process_dht_provide(local_node, request, response);
} else if (strcmp(request->sub_command, "get") == 0) {
// do a dht get
retVal = ipfs_core_http_process_dht_get(local_node, request, response);
}
return retVal;
}
int ipfs_core_http_process_swarm_connect(struct IpfsNode* local_node, struct HttpRequest* request, struct HttpResponse** resp) {
// get the address
if (request->arguments == NULL || request->arguments->total < 0)
return 0;
const char* address = (char*) libp2p_utils_vector_get(request->arguments, 0);
if (address == NULL)
return 0;
// TODO: see if we are already connected, or at least already have this peer in our peerstore
// attempt to connect
struct MultiAddress* ma = multiaddress_new_from_string(address);
if (ma == NULL) {
libp2p_logger_error("http_request", "swarm_connect: Unable to convert %s to a MultiAddress.\n", address);
return 0;
}
struct Libp2pPeer* new_peer = libp2p_peer_new_from_multiaddress(ma);
if (!libp2p_peer_connect(local_node->dialer, new_peer, local_node->peerstore, local_node->repo->config->datastore, 30)) {
libp2p_logger_error("http_request", "swarm_connect: Unable to connect to peer %s.\n", libp2p_peer_id_to_string(new_peer));
libp2p_peer_free(new_peer);
multiaddress_free(ma);
return 0;
}
*resp = ipfs_core_http_response_new();
struct HttpResponse* response = *resp;
if (response == NULL) {
libp2p_logger_error("http_response", "swarm_connect: Unable to allocate memory for the response.\n");
libp2p_peer_free(new_peer);
multiaddress_free(ma);
return 0;
}
response->content_type = "application/json";
char* json = "{ \"Strings\": [ \"%s\"] }";
response->bytes_size = strlen(json) + strlen(address) + 1;
response->bytes = (uint8_t*) malloc(response->bytes_size);
if (response->bytes == NULL) {
response->bytes_size = 0;
response->content_type = NULL;
libp2p_peer_free(new_peer);
multiaddress_free(ma);
return 0;
}
sprintf((char*)response->bytes, json, address);
// getting rid of the peer here will close the connection. That's not what we want
//libp2p_peer_free(new_peer);
multiaddress_free(ma);
return 1;
}
int ipfs_core_http_process_swarm(struct IpfsNode* local_node, struct HttpRequest* request, struct HttpResponse** response) {
int retVal = 0;
if (strcmp(request->sub_command, "connect") == 0) {
// connect to a peer
retVal = ipfs_core_http_process_swarm_connect(local_node, request, response);
}
return retVal;
}
/***
* Process the parameters passed in from an http request
* @param local_node the context
* @param request the request
* @param response the response
* @returns true(1) on success, false(0) otherwise.
*/
int ipfs_core_http_request_process(struct IpfsNode* local_node, struct HttpRequest* request, struct HttpResponse** response) {
if (request == NULL)
return 0;
int retVal = 0;
if (strcmp(request->command, "name") == 0) {
retVal = ipfs_core_http_process_name(local_node, request, response);
} else if (strcmp(request->command, "object") == 0) {
retVal = ipfs_core_http_process_object(local_node, request, response);
} else if (strcmp(request->command, "dht") == 0) {
retVal = ipfs_core_http_process_dht(local_node, request, response);
} else if (strcmp(request->command, "swarm") == 0) {
retVal = ipfs_core_http_process_swarm(local_node, request, response);
}
return retVal;
}
/**
* just builds the basics of an http api url
* @param local_node where to get the ip and port
* @returns a string in the form of "http://127.0.0.1:5001/api/v0" (or whatever was in the config file for the API)
*/
char* ipfs_core_http_request_build_url_start(struct IpfsNode* local_node) {
char* host = NULL;
char port[10] = "";
struct MultiAddress* ma = multiaddress_new_from_string(local_node->repo->config->addresses->api);
if (ma == NULL)
return NULL;
if (!multiaddress_get_ip_address(ma, &host)) {
multiaddress_free(ma);
return 0;
}
int portInt = multiaddress_get_ip_port(ma);
sprintf(port, "%d", portInt);
int len = 18 + strlen(host) + strlen(port);
char* retVal = malloc(len);
if (retVal != NULL) {
sprintf(retVal, "http://%s:%s/api/v0", host, port);
}
free(host);
multiaddress_free(ma);
return retVal;
}
/***
* Adds commands to url
* @param request the request
* @param url the starting url, and will hold the results (i.e. http://127.0.0.1:5001/api/v0/<command>/<sub_command>
* @returns true(1) on success, otherwise false(0)
*/
int ipfs_core_http_request_add_commands(struct HttpRequest* request, char** url) {
// command
int addl_length = strlen(request->command) + 2;
char* string1 = (char*) malloc(strlen(*url) + addl_length);
if (string1 != NULL) {
sprintf(string1, "%s/%s", *url, request->command);
free(*url);
*url = string1;
// sub_command
if (request->sub_command != NULL) {
addl_length = strlen(request->sub_command) + 2;
string1 = (char*) malloc(strlen(*url) + addl_length);
sprintf(string1, "%s/%s", *url, request->sub_command);
free(*url);
*url = string1;
}
}
return string1 != NULL;
}
/***
* Add parameters and arguments to the URL
* @param request the request
* @param url the url to continue to build
*/
int ipfs_core_http_request_add_parameters(struct HttpRequest* request, char** url) {
// params
if (request->params != NULL) {
for (int i = 0; i < request->params->total; i++) {
struct HttpParam* curr_param = (struct HttpParam*) libp2p_utils_vector_get(request->params, i);
int len = strlen(curr_param->name) + strlen(curr_param->value) + 2;
char* str = (char*) malloc(strlen(*url) + len);
if (str == NULL) {
return 0;
}
sprintf(str, "%s/%s=%s", *url, curr_param->name, curr_param->value);
free(*url);
*url = str;
}
}
// args
if (request->arguments != NULL) {
int isFirst = 1;
for(int i = 0; i < request->arguments->total; i++) {
char* arg = (char*) libp2p_utils_vector_get(request->arguments, i);
int len = strlen(arg) + strlen(*url) + 6;
char* str = (char*) malloc(len);
if (str == NULL)
return 0;
if (isFirst)
sprintf(str, "%s?arg=%s", *url, arg);
else
sprintf(str, "%s&arg=%s", *url, arg);
free(*url);
*url = str;
}
}
return 1;
}
struct curl_string {
char* ptr;
size_t len;
};
size_t curl_cb(void* ptr, size_t size, size_t nmemb, struct curl_string* str) {
size_t new_len = str->len + size * nmemb;
str->ptr = realloc(str->ptr, new_len + 1);
if (str->ptr != NULL) {
memcpy(str->ptr + str->len, ptr, size*nmemb);
str->ptr[new_len] = '\0';
str->len = new_len;
}
return size * nmemb;
}
/**
* Do an HTTP Get to the local API
* @param local_node the context
* @param request the request
* @param result the results
* @param result_size the size of the results
* @returns true(1) on success, false(0) on error
*/
int ipfs_core_http_request_get(struct IpfsNode* local_node, struct HttpRequest* request, char** result, size_t *result_size) {
if (request == NULL || request->command == NULL)
return 0;
char* url = ipfs_core_http_request_build_url_start(local_node);
if (url == NULL)
return 0;
if (!ipfs_core_http_request_add_commands(request, &url)) {
free(url);
return 0;
}
if (!ipfs_core_http_request_add_parameters(request, &url)) {
free(url);
return 0;
}
// do the GET using libcurl
CURL *curl;
CURLcode res;
struct curl_string s;
s.len = 0;
s.ptr = malloc(1);
s.ptr[0] = '\0';
curl = curl_easy_init();
if (!curl) {
return 0;
}
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
if (res == CURLE_OK) {
if (strcmp(s.ptr, "404 page not found") != 0) {
*result = s.ptr;
*result_size = s.len;
}
else
res = -1;
} else {
libp2p_logger_error("http_request", "Results of [%s] returned failure. Return value: %d.\n", url, res);
if (s.ptr != NULL)
free(s.ptr);
}
return res == CURLE_OK;
}
/**
* Do an HTTP Post to the local API
* @param local_node the context
* @param request the request
* @param result the results
* @param result_size the size of the results
* @param data the array with post data
* @param data_size the data length
* @returns true(1) on success, false(0) on error
*/
int ipfs_core_http_request_post(struct IpfsNode* local_node, struct HttpRequest* request, char** result, size_t* result_size, char *data, size_t data_size) {
if (request == NULL || request->command == NULL || data == NULL)
return 0;
char* url = ipfs_core_http_request_build_url_start(local_node);
if (url == NULL)
return 0;
if (!ipfs_core_http_request_add_commands(request, &url)) {
free(url);
return 0;
}
if (!ipfs_core_http_request_add_parameters(request, &url)) {
free(url);
return 0;
}
// do the POST using libcurl
CURL *curl;
CURLcode res;
struct curl_string s;
s.len = 0;
s.ptr = malloc(1);
s.ptr[0] = '\0';
struct curl_httppost *post = NULL, *last = NULL;
CURLFORMcode curl_form_ret = curl_formadd(&post, &last,
CURLFORM_COPYNAME, "filename",
CURLFORM_PTRCONTENTS, data,
CURLFORM_CONTENTTYPE, "application/octet-stream",
CURLFORM_FILENAME, "",
CURLFORM_CONTENTSLENGTH, data_size,
CURLFORM_END);
if (CURL_FORMADD_OK != curl_form_ret) {
// i'm always getting curl_form_ret == 4 here
// it means CURL_FORMADD_UNKNOWN_OPTION
// what i'm doing wrong?
fprintf(stderr, "curl_form_ret = %d\n", (int)curl_form_ret);
return 0;
}
curl = curl_easy_init();
if (!curl) {
return 0;
}
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
curl_easy_setopt(curl, CURLOPT_HTTPPOST, post);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
if (res == CURLE_OK) {
if (strcmp(s.ptr, "404 page not found") != 0) {
*result = s.ptr;
*result_size = s.len;
}
else
res = -1;
} else {
//libp2p_logger_error("http_request", "Results of [%s] returned failure. Return value: %d.\n", url, res);
fprintf(stderr, "Results of [%s] returned failure. Return value: %d.\n", url, res);
if (s.ptr != NULL)
free(s.ptr);
}
return res == CURLE_OK;
}

View file

@ -1,191 +0,0 @@
#include <stdlib.h>
#include <pthread.h>
#include "libp2p/conn/dialer.h"
#include "libp2p/identify/identify.h"
#include "libp2p/net/multistream.h"
#include "libp2p/utils/vector.h"
#include "libp2p/secio/secio.h"
#include "libp2p/routing/dht_protocol.h"
#include "libp2p/yamux/yamux.h"
#include "ipfs/core/api.h"
#include "ipfs/core/client_api.h"
#include "ipfs/core/ipfs_node.h"
#include "ipfs/exchange/bitswap/bitswap.h"
#include "ipfs/journal/journal.h"
struct IpfsNode* ipfs_node_new() {
struct IpfsNode* node = malloc(sizeof(struct IpfsNode));
if (node != NULL) {
node->blockstore = NULL;
node->exchange = NULL;
node->identity = NULL;
node->mode = MODE_OFFLINE;
node->peerstore = NULL;
node->protocol_handlers = NULL;
node->providerstore = NULL;
node->repo = NULL;
node->routing = NULL;
node->api_context = NULL;
}
return node;
}
struct Libp2pVector* ipfs_node_online_build_protocol_handlers(struct IpfsNode* node) {
struct Libp2pVector* retVal = libp2p_utils_vector_new(1);
if (retVal != NULL) {
// secio
libp2p_utils_vector_add(retVal, libp2p_secio_build_protocol_handler(&node->identity->private_key, node->peerstore));
// journal
libp2p_utils_vector_add(retVal, ipfs_journal_build_protocol_handler(node));
// kademlia
libp2p_utils_vector_add(retVal, libp2p_routing_dht_build_protocol_handler(node->peerstore, node->providerstore, node->repo->config->datastore, node->repo->config->filestore));
// bitswap
libp2p_utils_vector_add(retVal, ipfs_bitswap_build_protocol_handler(node));
// multistream
libp2p_utils_vector_add(retVal, libp2p_net_multistream_build_protocol_handler(retVal));
// yamux
libp2p_utils_vector_add(retVal, libp2p_yamux_build_protocol_handler());
// identify
libp2p_utils_vector_add(retVal, libp2p_identify_build_protocol_handler(node->identity->peer->id, node->identity->peer->id_size));
}
return retVal;
}
int ipfs_node_online_protocol_handlers_free(struct Libp2pVector* handlers) {
for(int i = 0; i < handlers->total; i++) {
struct Libp2pProtocolHandler* current = (struct Libp2pProtocolHandler*) libp2p_utils_vector_get(handlers, i);
current->Shutdown(current->context);
free(current);
}
libp2p_utils_vector_free(handlers);
return 1;
}
/***
* build an online IpfsNode
* @param repo_path where the IPFS repository directory is
* @param node the completed IpfsNode struct
* @returns true(1) on success
*/
int ipfs_node_online_new(const char* repo_path, struct IpfsNode** node) {
struct FSRepo* fs_repo = NULL;
*node = ipfs_node_new();
if(*node == NULL)
return 0;
struct IpfsNode* local_node = *node;
// build the struct
if (!ipfs_repo_fsrepo_new(repo_path, NULL, &fs_repo)) {
ipfs_node_free(local_node);
*node = NULL;
return 0;
}
// open the repo
if (!ipfs_repo_fsrepo_open(fs_repo)) {
ipfs_node_free(local_node);
*node = NULL;
return 0;
}
// fill in the node
local_node->repo = fs_repo;
local_node->identity = fs_repo->config->identity;
local_node->peerstore = libp2p_peerstore_new(local_node->identity->peer);
local_node->providerstore = libp2p_providerstore_new(fs_repo->config->datastore, local_node->identity->peer);
local_node->blockstore = ipfs_blockstore_new(fs_repo);
local_node->protocol_handlers = ipfs_node_online_build_protocol_handlers(local_node);
local_node->mode = MODE_OFFLINE;
local_node->routing = ipfs_routing_new_online(local_node, &fs_repo->config->identity->private_key);
local_node->exchange = ipfs_bitswap_new(local_node);
local_node->swarm = libp2p_swarm_new(local_node->protocol_handlers, local_node->repo->config->datastore, local_node->repo->config->filestore);
local_node->dialer = libp2p_conn_dialer_new(local_node->identity->peer, local_node->peerstore, &local_node->identity->private_key, local_node->swarm);
// fire up the API
api_start(local_node, 10, 5);
return 1;
}
/***
* build an offline IpfsNode
* @param repo_path where the IPFS repository directory is
* @param node the completed IpfsNode struct
* @returns true(1) on success
*/
int ipfs_node_offline_new(const char* repo_path, struct IpfsNode** node) {
struct FSRepo* fs_repo = NULL;
*node = ipfs_node_new();
if(*node == NULL)
return 0;
struct IpfsNode* local_node = *node;
// build the struct
if (!ipfs_repo_fsrepo_new(repo_path, NULL, &fs_repo)) {
ipfs_node_free(local_node);
*node = NULL;
return 0;
}
// open the repo
if (!ipfs_repo_fsrepo_open(fs_repo)) {
ipfs_node_free(local_node);
*node = NULL;
return 0;
}
// fill in the node
local_node->repo = fs_repo;
local_node->identity = fs_repo->config->identity;
local_node->peerstore = libp2p_peerstore_new(local_node->identity->peer);
local_node->providerstore = libp2p_providerstore_new(fs_repo->config->datastore, local_node->identity->peer);
local_node->blockstore = ipfs_blockstore_new(fs_repo);
local_node->protocol_handlers = ipfs_node_online_build_protocol_handlers(local_node);
local_node->mode = MODE_OFFLINE;
local_node->routing = ipfs_routing_new_offline(local_node, &fs_repo->config->identity->private_key);
local_node->exchange = ipfs_bitswap_new(local_node);
local_node->swarm = libp2p_swarm_new(local_node->protocol_handlers, local_node->repo->config->datastore, local_node->repo->config->filestore);
local_node->dialer = libp2p_conn_dialer_new(local_node->identity->peer, local_node->peerstore, &local_node->identity->private_key, local_node->swarm);
if (api_running(local_node))
local_node->mode = MODE_API_AVAILABLE;
return 1;
}
/***
* Free resources from the creation of an IpfsNode
* @param node the node to free
* @returns true(1)
*/
int ipfs_node_free(struct IpfsNode* node) {
if (node != NULL) {
if (node->api_context != NULL && node->api_context->api_thread != 0)
api_stop(node);
if (node->exchange != NULL) {
node->exchange->Close(node->exchange);
}
if (node->providerstore != NULL)
libp2p_providerstore_free(node->providerstore);
if (node->peerstore != NULL)
libp2p_peerstore_free(node->peerstore);
if (node->repo != NULL)
ipfs_repo_fsrepo_free(node->repo);
if (node->protocol_handlers != NULL)
ipfs_node_online_protocol_handlers_free(node->protocol_handlers);
if (node->mode == MODE_ONLINE) {
ipfs_routing_online_free(node->routing);
}
if (node->mode == MODE_OFFLINE || node->mode == MODE_API_AVAILABLE) {
ipfs_routing_offline_free(node->routing);
}
if (node->blockstore != NULL) {
ipfs_blockstore_free(node->blockstore);
}
free(node);
}
return 1;
}

View file

@ -1,42 +0,0 @@
#include "ipfs/core/net.h"
/**
* Do a socket accept
* @param listener the listener
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_core_net_accept(struct IpfsListener listener) {
//TODO: Implement this
return 0;
}
/**
* Listen using a particular protocol
* @param node the node
* @param protocol the protocol to use
* @param listener the results
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_core_net_listen(struct IpfsNode* node, char* protocol, struct IpfsListener* listener){
// TODO: Implement this
return 0;
}
/***
* Dial a peer
* @param node this node
* @param peer_id who to dial (null terminated string)
* @param protocol the protocol to use
* @param stream the resultant stream
* @returns true(1) on success, otherwise false(0)
*/
int ipsf_core_net_dial(const struct IpfsNode* node, const char* peer_id, const char* protocol, struct Stream* stream) {
//TODO: Implement this
// get the multiaddress from the peerstore
struct Libp2pPeer* peer = libp2p_peerstore_get_peer(node->peerstore, peer_id, strlen(peer_id));
// attempt to connect
// attempt to use the protocol passed in
return 0;
}

View file

@ -1,238 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "libp2p/conn/session.h"
#include "libp2p/net/connectionstream.h"
#include "libp2p/net/multistream.h"
#include "libp2p/net/p2pnet.h"
#include "libp2p/net/protocol.h"
#include "libp2p/nodeio/nodeio.h"
#include "libp2p/os/utils.h"
#include "libp2p/record/message.h"
#include "libp2p/routing/dht_protocol.h"
#include "libp2p/secio/secio.h"
#include "libp2p/utils/logger.h"
#include "libp2p/swarm/swarm.h"
#include "ipfs/core/daemon.h"
#include "ipfs/core/ipfs_node.h"
#include "ipfs/exchange/bitswap/network.h"
#include "ipfs/journal/journal.h"
#include "ipfs/merkledag/merkledag.h"
#include "ipfs/merkledag/node.h"
#include "ipfs/routing/routing.h"
#include "ipfs/util/thread_pool.h"
#include "libp2p/swarm/swarm.h"
#define BUF_SIZE 4096
// this should be set to 5 for normal operation, perhaps higher for debugging purposes
#define DEFAULT_NETWORK_TIMEOUT 5
static int null_shutting_down = 0;
/***
* Listens on a particular stream, and marshals the request
* @param stream the stream to listen to
* @param protocol_handlers a vector of protocol handlers
* @returns <0 on error, 0 if we shouldn't handle this anymore, or 1 on success
*/
int ipfs_null_listen_and_handle(struct Stream* stream, struct Libp2pVector* protocol_handlers) {
struct StreamMessage* results = NULL;
int retVal = 0;
// Read from the network
if (!stream->read(stream->stream_context, &results, DEFAULT_NETWORK_TIMEOUT)) {
libp2p_logger_error("null", "Unable to read from network. Exiting.\n");
return retVal;
}
if (results != NULL) {
libp2p_logger_debug("null", "Attempting to marshal %d bytes from network.\n", results->data_size);
retVal = libp2p_protocol_marshal(results, stream, protocol_handlers);
libp2p_logger_debug("null", "The return value from the attempt to marshal %d bytes was %d.\n", results->data_size, retVal);
libp2p_stream_message_free(results);
} else {
libp2p_logger_debug("null", "Attempted read, but results were null. This is normal.\n");
}
return retVal;
}
/**
* We've received a new connection. Find out what they want.
*
* @param ptr a pointer to a null_connection_params struct
*/
void ipfs_null_connection (void *ptr) {
struct null_connection_params *connection_param = (struct null_connection_params*) ptr;
int retVal = 0;
struct SessionContext* session = libp2p_session_context_new();
if (session == NULL) {
libp2p_logger_error("null", "Unable to allocate SessionContext. Out of memory?\n");
return;
}
session->datastore = connection_param->local_node->repo->config->datastore;
session->filestore = connection_param->local_node->repo->config->filestore;
session->host = connection_param->ip;
session->port = connection_param->port;
session->insecure_stream = libp2p_net_connection_new(connection_param->file_descriptor, connection_param->ip, connection_param->port, session);
session->default_stream = session->insecure_stream;
libp2p_logger_info("null", "Connection %d, count %d\n", connection_param->file_descriptor, *(connection_param->count));
// try to read from the network
// handle the call
for(;;) {
// Read from the network
retVal = ipfs_null_listen_and_handle(session->default_stream, connection_param->local_node->protocol_handlers);
if (retVal < 0) {
// exit the loop on error
libp2p_logger_debug("null", "Exiting loop due to retVal being %d.\n", retVal);
break;
}
} // end of loop
(*(connection_param->count))--; // update counter.
if (connection_param->ip != NULL)
free(connection_param->ip);
free (connection_param);
return;
}
int ipfs_null_do_maintenance(struct IpfsNode* local_node, struct Libp2pPeer* peer) {
if (peer == NULL) {
return 0;
}
if (peer->is_local) {
return 1;
}
// Is this peer one of our backup partners?
struct ReplicationPeer* replication_peer = repo_config_get_replication_peer(local_node->repo->config->replication, peer);
long long announce_secs = local_node->repo->config->replication->announce_minutes * 60;
// If so, has there been enough time since the last attempt a backup?
if (replication_peer != NULL) {
announce_secs -= os_utils_gmtime() - replication_peer->lastConnect;
libp2p_logger_debug("null", "Checking to see if we should send backup notification to peer %s. Time since last backup: %lld.\n", libp2p_peer_id_to_string(replication_peer->peer), announce_secs);
}
// should we attempt to connect if we're not already?
if (replication_peer != NULL && local_node->repo->config->replication->announce && announce_secs < 0) {
// try to connect if we aren't already
if (peer->connection_type != CONNECTION_TYPE_CONNECTED) {
if (!libp2p_peer_connect(local_node->dialer, peer, local_node->peerstore, local_node->repo->config->datastore, 2)) {
return 0;
}
}
// attempt a backup, don't forget to reset timer
libp2p_logger_debug("null", "Attempting a sync of node %s.\n", libp2p_peer_id_to_string(peer));
ipfs_journal_sync(local_node, replication_peer);
libp2p_logger_debug("null", "Sync message sent. Maintenance complete for node %s.\n", libp2p_peer_id_to_string(peer));
} else {
unsigned long long last_comm = libp2p_peer_last_comm(peer);
if (os_utils_gmtime() - last_comm > 180) {
// try a ping, but only if we're connected
libp2p_logger_debug("null", "Attempting ping of %s.\n", libp2p_peer_id_to_string(peer));
if (peer->connection_type == CONNECTION_TYPE_CONNECTED && !local_node->routing->Ping(local_node->routing, peer)) {
libp2p_logger_debug("null", "Attempted ping of %s failed.\n", peer->id);
peer->connection_type = CONNECTION_TYPE_NOT_CONNECTED;
}
}
}
return 1;
}
/***
* Called by the daemon to listen for connections
* @param ptr a pointer to an IpfsNodeListenParams struct
* @returns nothing useful.
*/
void* ipfs_null_listen (void *ptr)
{
null_shutting_down = 0;
int socketfd, s, count = 0;
//threadpool thpool = thpool_init(25);
struct IpfsNodeListenParams *listen_param;
listen_param = (struct IpfsNodeListenParams*) ptr;
if ((socketfd = socket_listen(socket_tcp4(), &(listen_param->ipv4), &(listen_param->port))) <= 0) {
libp2p_logger_error("null", "Failed to init null router. Address: %d, Port: %d\n", listen_param->ipv4, listen_param->port);
return (void*) 2;
}
libp2p_logger_info("null", "Ipfs listening on %d\n", listen_param->port);
// when we have nothing to do, check on the connections to see if we're still connected
struct Libp2pLinkedList* current_peer_entry = NULL;
if (listen_param->local_node->peerstore->head_entry != NULL)
current_peer_entry = listen_param->local_node->peerstore->head_entry;
// the main loop, listening for new connections
for (;;) {
//libp2p_logger_debug("null", "%s Attempting socket read with fd %d.\n", listen_param->local_node->identity->peer->id, socketfd);
int numDescriptors = socket_read_select4(socketfd, 2);
if (null_shutting_down) {
libp2p_logger_debug("null", "%s null_listen shutting down.\n", listen_param->local_node->identity->peer->id);
break;
}
if (numDescriptors > 0) {
s = socket_accept4(socketfd, &(listen_param->ipv4), &(listen_param->port));
if (count >= CONNECTIONS) { // limit reached.
close (s);
continue;
}
// add the new connection to the swarm
libp2p_swarm_add_connection(listen_param->local_node->swarm, s, listen_param->ipv4, listen_param->port);
/*
count++;
connection_param = malloc (sizeof (struct null_connection_params));
if (connection_param) {
connection_param->file_descriptor = s;
connection_param->count = &count;
connection_param->local_node = listen_param->local_node;
connection_param->port = listen_param->port;
connection_param->ip = malloc(INET_ADDRSTRLEN);
if (connection_param->ip == NULL) {
// we are out of memory
free(connection_param);
continue;
}
if (inet_ntop(AF_INET, &(listen_param->ipv4), connection_param->ip, INET_ADDRSTRLEN) == NULL) {
free(connection_param->ip);
connection_param->ip = NULL;
connection_param->port = 0;
}
// Create pthread for ipfs_null_connection.
thpool_add_work(thpool, ipfs_null_connection, connection_param);
}
*/
} else {
// timeout... do maintenance
//struct PeerEntry* entry = current_peer_entry->item;
// JMJ Debugging
//ipfs_null_do_maintenance(listen_param->local_node, entry->peer);
if (current_peer_entry != NULL)
current_peer_entry = current_peer_entry->next;
if (current_peer_entry == NULL)
current_peer_entry = listen_param->local_node->peerstore->head_entry;
}
}
//thpool_destroy(thpool);
close(socketfd);
return (void*) 2;
}
int ipfs_null_shutdown() {
null_shutting_down = 1;
return 1;
}

View file

@ -1,124 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <pthread.h>
#include "libp2p/net/p2pnet.h"
#include "libp2p/net/multistream.h"
#include "libp2p/record/message.h"
#include "libp2p/secio/secio.h"
#include "libp2p/routing/dht_protocol.h"
#include "ipfs/repo/fsrepo/fs_repo.h"
#include "ipfs/repo/init.h"
#include "ipfs/core/ipfs_node.h"
#include "ipfs/routing/routing.h"
#include "ipfs/importer/resolver.h"
#include "multiaddr/multiaddr.h"
#define BUF_SIZE 4096
int ipfs_ping (int argc, char **argv)
{
int retVal = 0;
struct IpfsNode local_node;
struct Libp2pPeer* peer_to_ping = NULL;
char* id = NULL;
struct FSRepo* fs_repo = NULL;
char* repo_path = NULL;
struct timeval time1, time2;
int time_us;
// sanity check
local_node.peerstore = NULL;
local_node.providerstore = NULL;
if (argc < 3)
goto exit;
if (!ipfs_repo_get_directory(argc, argv, &repo_path)) {
fprintf(stderr, "Unable to open repo: %s\n", repo_path);
return 0;
}
// read the configuration
if (!ipfs_repo_fsrepo_new(NULL, NULL, &fs_repo))
goto exit;
// open the repository and read the file
if (!ipfs_repo_fsrepo_open(fs_repo))
goto exit;
local_node.identity = fs_repo->config->identity;
local_node.repo = fs_repo;
local_node.mode = MODE_ONLINE;
local_node.routing = ipfs_routing_new_online(&local_node, &fs_repo->config->identity->private_key);
local_node.peerstore = libp2p_peerstore_new(local_node.identity->peer);
local_node.providerstore = libp2p_providerstore_new(fs_repo->config->datastore, fs_repo->config->identity->peer);
if (local_node.routing->Bootstrap(local_node.routing) != 0)
goto exit;
if (strstr(argv[2], "Qm") == &argv[2][0]) {
// resolve the peer id
fprintf (stderr, "Looking up peer %s\n", argv[2]);
peer_to_ping = ipfs_resolver_find_peer(argv[2], &local_node);
} else {
// perhaps they passed an IP and port
if (argc >= 3) {
char* str = malloc(strlen(argv[2]) + strlen(argv[3]) + 100);
if (str == NULL) {
// memory issue
goto exit;
}
sprintf(str, "/ip4/%s/tcp/%s", argv[2], argv[3]);
peer_to_ping = libp2p_peer_new();
if (peer_to_ping) {
peer_to_ping->addr_head = libp2p_utils_linked_list_new();
peer_to_ping->addr_head->item = multiaddress_new_from_string(str);
peer_to_ping->id = str;
peer_to_ping->id_size = strlen(str);
}
}
//TODO: Error checking
}
if (peer_to_ping == NULL)
goto exit;
id = malloc(peer_to_ping->id_size + 1);
if (id) {
memcpy(id, peer_to_ping->id, peer_to_ping->id_size);
id[peer_to_ping->id_size] = 0;
fprintf (stderr, "PING %s.\n", id);
}
for (;;) {
gettimeofday(&time1, NULL);
if (!local_node.routing->Ping(local_node.routing, peer_to_ping)) {
fprintf(stderr, "Unable to ping %s\n", id);
goto exit;
}
gettimeofday(&time2, NULL);
// calculate microseconds
time_us = (time2.tv_sec - time1.tv_sec) * 1000000;
time_us += (time2.tv_usec - time1.tv_usec);
fprintf (stderr, "Pong received: time=%d.%03d ms\n", time_us / 1000, time_us % 1000);
if (time_us < 1000000) { // if the ping took less than a second...
sleep(1);
}
}
retVal = 1;
exit:
if (id != NULL)
free(id);
if (fs_repo != NULL)
ipfs_repo_fsrepo_free(fs_repo);
if (local_node.peerstore != NULL)
libp2p_peerstore_free(local_node.peerstore);
if (local_node.providerstore != NULL)
libp2p_providerstore_free(local_node.providerstore);
return retVal;
}

View file

@ -1,74 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include "libp2p/utils/logger.h"
#include "ipfs/core/ipfs_node.h"
#include "ipfs/core/swarm.h"
#include "ipfs/core/http_request.h"
/***
* Connect to a swarm
* @param local_node the local node
* @param address the address of the remote
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_swarm_connect(struct IpfsNode* local_node, const char* address) {
char* response = NULL;
size_t response_size;
// use the API to connect
struct HttpRequest* request = ipfs_core_http_request_new();
if (request == NULL)
return 0;
request->command = "swarm";
request->sub_command = "connect";
libp2p_utils_vector_add(request->arguments, address);
int retVal = ipfs_core_http_request_get(local_node, request, &response, &response_size);
if (response != NULL && response_size > 0) {
fwrite(response, 1, response_size, stdout);
free(response);
}
ipfs_core_http_request_free(request);
return retVal;
}
/***
* Handle command line swarm call
*/
int ipfs_swarm (struct CliArguments* args) {
int retVal = 0;
struct IpfsNode* client_node = NULL;
if (args->argc < (args->verb_index + 2)) {
libp2p_logger_error("swarm", "Not enough command line arguments. Should be \"swarm connect\" or \"swarm disconnect\" etc.\n");
goto exit;
}
// make sure API is running
if (!ipfs_node_offline_new(args->config_dir, &client_node)) {
libp2p_logger_error("swarm", "Unable to create offline node.\n");
goto exit;
}
if (client_node->mode != MODE_API_AVAILABLE) {
libp2p_logger_error("swarm", "API must be running.\n");
goto exit;
}
const char* which = args->argv[args->verb_index + 1];
const char* path = args->argv[args->verb_index + 2];
// determine what we're doing
if (strcmp(which, "connect") == 0) {
retVal = ipfs_swarm_connect(client_node, path);
} else if (strcmp(which, "disconnect") == 0) {
libp2p_logger_error("swarm", "Swarm disconnect not implemented yet.\n");
retVal = 0;
} else {
libp2p_logger_error("swarm", "Nothing useful found on command line. Should be \"swarm connect\" or \"swarm disconnect\".\n");
goto exit;
}
exit:
// shut everything down
ipfs_node_free(client_node);
return retVal;
}

View file

@ -1,5 +1,5 @@
CC = gcc
CFLAGS = -O0 -I../include -I../c-libp2p/include -I../c-libp2p/c-multihash/include -I../c-libp2p/c-multiaddr/include -I../lmdb/libraries/liblmdb -I../c-libp2p/c-protobuf -Wall -std=gnu99
CFLAGS = -O0 -I../include -I../../c-libp2p/include -I../../c-multihash/include -I../../c-multiaddr/include -I../../lmdb/libraries/liblmdb
ifdef DEBUG
CFLAGS += -g3

View file

@ -1,12 +1,10 @@
/**
* Some code to help with the datastore / blockstore interface
* NOTE: the datastore stores things under a multihash key
*/
#include <stdlib.h>
#include "libp2p/crypto/encoding/base32.h"
#include "ipfs/datastore/ds_helper.h"
/**
* Generate a base32 key based on the passed in binary_array (which is normally a multihash)
* Generate a key based on the passed in binary_array
* @param binary_array what to base the key on
* @param array_length the size of the binary array
* @param results where the key will be put
@ -14,8 +12,8 @@
* @param results_length the length of the generated key
* @returns true(1) on success
*/
int ipfs_datastore_helper_ds_key_from_binary(const unsigned char* binary_array, size_t array_length,
unsigned char* results, size_t max_results_length, size_t* results_length) {
int ipfs_datastore_helper_ds_key_from_binary(unsigned char* binary_array, size_t array_length,
char* results, size_t max_results_length, size_t* results_length) {
size_t encoded_length = libp2p_crypto_encoding_base32_encode_size(array_length);
if (encoded_length > max_results_length)
@ -32,7 +30,7 @@ int ipfs_datastore_helper_ds_key_from_binary(const unsigned char* binary_array,
}
/**
* Generate a binary array (normally a multihash) based on the passed in datastore key
* Generate a binary array based on the passed in datastore key
* @param ds_key the base32 encoded key
* @param key_length the length of the base32 "string"
* @param binary_array where to put the decoded value
@ -40,7 +38,7 @@ int ipfs_datastore_helper_ds_key_from_binary(const unsigned char* binary_array,
* @param completed_binary_array_length the length of what was written to the binary_array
* @returns true(1) on success
*/
int ipfs_datastore_helper_binary_from_ds_key(const unsigned char* ds_key, size_t key_length, unsigned char* binary_array,
int ipfs_datastore_helper_binary_from_ds_key(unsigned char* ds_key, size_t key_length, unsigned char* binary_array,
size_t max_binary_array_length, size_t* completed_binary_array_length) {
size_t decoded_length = libp2p_crypto_encoding_base32_decode_size(key_length);
@ -55,41 +53,3 @@ int ipfs_datastore_helper_binary_from_ds_key(const unsigned char* ds_key, size_t
}
return 1;
}
/***
* Add a record in the datastore based on a block
* @param block the block
* @param datastore the Datastore
* @reutrns true(1) on success, false(0) otherwise
*/
int ipfs_datastore_helper_add_block_to_datastore(struct Block* block, struct Datastore* datastore) {
struct DatastoreRecord* rec = libp2p_datastore_record_new();
if (rec == NULL)
return 0;
rec->key_size = block->cid->hash_length;
rec->key = (uint8_t*) malloc(rec->key_size);
if (rec->key == NULL) {
libp2p_datastore_record_free(rec);
return 0;
}
memcpy(rec->key, block->cid->hash, rec->key_size);
rec->timestamp = 0;
// convert the key to base32, and store it in the DatabaseRecord->value section
size_t fs_key_length = 100;
uint8_t fs_key[fs_key_length];
if (!ipfs_datastore_helper_ds_key_from_binary(block->cid->hash, block->cid->hash_length, fs_key, fs_key_length, &fs_key_length)) {
libp2p_datastore_record_free(rec);
return 0;
}
rec->value_size = fs_key_length;
rec->value = malloc(rec->value_size);
if (rec->value == NULL) {
libp2p_datastore_record_free(rec);
return 0;
}
memcpy(rec->value, fs_key, rec->value_size);
int retVal = datastore->datastore_put(rec, datastore);
libp2p_datastore_record_free(rec);
return retVal;
}

View file

@ -1,18 +0,0 @@
CC = gcc
CFLAGS = -O0 -I../include -I../c-libp2p/include -I../c-libp2p/c-multihash/include -I../c-libp2p/c-multiaddr/include -I../c-libp2p/c-protobuf -Wall -std=gnu99
ifdef DEBUG
CFLAGS += -g3
endif
LFLAGS =
DEPS =
OBJS = dnslink.o
%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
all: $(OBJS)
clean:
rm -f *.o

View file

@ -1,407 +0,0 @@
/*
Package dnslink implements a dns link resolver. dnslink is a basic
standard for placing traversable links in dns itself. See dnslink.info
A dnslink is a path link in a dns TXT record, like this:
dnslink=/ipfs/QmR7tiySn6vFHcEjBeZNtYGAFh735PJHfEMdVEycj9jAPy
For example:
> dig TXT ipfs.io
ipfs.io. 120 IN TXT dnslink=/ipfs/QmR7tiySn6vFHcEjBeZNtYGAFh735PJHfEMdVEycj9jAPy
This package eases resolving and working with thse dns links. For example:
import (
dnslink "github.com/jbenet/go-dnslink"
)
link, err := dnslink.Resolve("ipfs.io")
// link = "/ipfs/QmR7tiySn6vFHcEjBeZNtYGAFh735PJHfEMdVEycj9jAPy"
It even supports recursive resolution. Suppose you have three domains with
dnslink records like these:
> dig TXT foo.com
foo.com. 120 IN TXT dnslink=/dns/bar.com/f/o/o
> dig TXT bar.com
bar.com. 120 IN TXT dnslink=/dns/long.test.baz.it/b/a/r
> dig TXT long.test.baz.it
long.test.baz.it. 120 IN TXT dnslink=/b/a/z
Expect these resolutions:
dnslink.ResolveN("long.test.baz.it", 0) // "/dns/long.test.baz.it"
dnslink.Resolve("long.test.baz.it") // "/b/a/z"
dnslink.ResolveN("bar.com", 1) // "/dns/long.test.baz.it/b/a/r"
dnslink.Resolve("bar.com") // "/b/a/z/b/a/r"
dnslink.ResolveN("foo.com", 1) // "/dns/bar.com/f/o/o/"
dnslink.ResolveN("foo.com", 2) // "/dns/long.test.baz.it/b/a/r/f/o/o/"
dnslink.Resolve("foo.com") // "/b/a/z/b/a/r/f/o/o"
*/
#include <stdlib.h>
#include <string.h>
#ifdef __MINGW32__
#include <stdint.h>
#define u_char unsigned char
#define u_int16_t uint16_t
#define u_int32_t uint32_t
#define NS_MAXDNAME 1025 /*%< maximum domain name */
#define ns_c_in 1
#define ns_t_txt 16
typedef enum __ns_sect {
ns_s_qd = 0, /*%< Query: Question. */
ns_s_zn = 0, /*%< Update: Zone. */
ns_s_an = 1, /*%< Query: Answer. */
ns_s_pr = 1, /*%< Update: Prerequisites. */
ns_s_ns = 2, /*%< Query: Name servers. */
ns_s_ud = 2, /*%< Update: Update. */
ns_s_ar = 3, /*%< Query|Update: Additional records. */
ns_s_max = 4
} ns_sect;
typedef struct __ns_msg {
const unsigned char *_msg, *_eom;
u_int16_t _id, _flags, _counts[ns_s_max];
const unsigned char *_sections[ns_s_max];
ns_sect _sect;
int _rrnum;
const unsigned char *_msg_ptr;
} ns_msg;
typedef struct __ns_rr {
char name[NS_MAXDNAME];
u_int16_t type;
u_int16_t rr_class;
u_int32_t ttl;
u_int16_t rdlength;
const u_char * rdata;
} ns_rr;
#else
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
#endif
#include "ipfs/namesys/namesys.h"
#define IPFS_DNSLINK_C
#include "ipfs/dnslink/dnslink.h"
#include "ipfs/cid/cid.h"
#include "ipfs/path/path.h"
int ipfs_dns (int argc, char **argv)
{
#ifdef __MINGW32__
fprintf (stderr, "Sorry, dns has not yet been implemented for the Windows version.\n");
return -1;
}
#else
int err, r=0, i;
char **txt, *path, *param;
if (argc == 4 && strcmp ("-r", argv[2])==0) {
r = 1;
argc--; argv++;
}
if (argc != 3) {
fprintf (stderr, "usage: ipfs dns [-r] dns.name.com\n");
return -1;
}
param = malloc (strlen (argv[2]) + 1);
if (!param) {
fprintf (stderr, "memory allocation failed.\n");
return 1;
}
strcpy (param, argv[2]);
for (i = 0 ; i < DefaultDepthLimit ; i++) {
if (memcmp(param, "/ipns/", 6) == 0) {
err = ipfs_dnslink_resolv_lookupTXT (&txt, param+6);
} else {
err = ipfs_dnslink_resolv_lookupTXT (&txt, param);
}
free (param);
if (err) {
fprintf (stderr, "dns lookupTXT: %s\n", Err[err]);
return err;
}
err = ipfs_dnslink_parse_txt(&path, *txt);
if (err) {
free (*txt);
free (txt);
fprintf (stderr, "dns parse_txt: %s\n", Err[err]);
return err;
}
free (*txt);
free (txt);
param = path;
if (! r) {
// not recursive.
break;
}
if (memcmp(path, "/ipfs/", 6) == 0) {
break;
}
} while (--r);
fprintf (stdout, "%s\n", param);
free (param);
return 0;
}
#endif // MINGW
// ipfs_dnslink_resolve resolves the dnslink at a particular domain. It will
// recursively keep resolving until reaching the defaultDepth of Resolver. If
// the depth is reached, ipfs_dnslink_resolve will return the last value
// retrieved, and ErrResolveLimit. If TXT records are found but are not valid
// dnslink records, ipfs_dnslink_resolve will return ErrInvalidDNSLink.
// ipfs_dnslink_resolve will check every TXT record returned. If resolution
// fails otherwise, ipfs_dnslink_resolve will return ErrResolveFailed
int ipfs_dnslink_resolve (char **p, char *domain)
{
return ipfs_dnslink_resolve_n (p, domain, DefaultDepthLimit);
}
// ipfs_dnslink_lookup_txt is a function that looks up a TXT record in some dns resolver.
// This is useful for testing or passing your own dns resolution process, which
// could take into account non-standard TLDs like .bit, .onion, .ipfs, etc.
int (*ipfs_dnslink_lookup_txt)(char ***txt, char *name) = NULL;
// ipfs_dnslink_resolve_n is just like Resolve, with the option to specify a
// maximum resolution depth.
int ipfs_dnslink_resolve_n (char **p, char *d, int depth)
{
int err, i, l;
char *rest, **link, tail[500], buf[500], domain[500];
char dns_prefix[] = "/dns/";
domain[sizeof(domain)-1] = '\0';
strncpy (domain, d, sizeof(domain) - 1);
for (i=0 ; i < depth ; i++) {
err = ipfs_dnslink_resolve_once (&link, domain);
if (err) {
return err;
}
// if does not have /dns/ as a prefix, done.
if (memcmp (*link, dns_prefix, sizeof(dns_prefix) - 1)!=0) {
l = strlen(*link) + strlen(tail);
*p = malloc(l + 1);
if (!*p) {
free(*link);
free(link);
return ErrAllocFailed;
}
*p[l] = '\0';
strncpy(*p, *link, l);
free(*link);
free(link);
strncat(*p, tail, l - strlen(*p));
return 0; // done
}
// keep resolving
err = ipfs_dnslink_parse_link_domain (&d, &rest, *link);
free (*link);
free (link);
if (err) {
*p = NULL;
return err;
}
strncpy (domain, d, sizeof(domain) - 1);
free (d);
strncpy (buf, tail, sizeof(buf) - 1);
strncpy (tail, rest, sizeof(tail) - 1);
strncat (tail, buf, sizeof(tail) - 1 - strlen(tail));
}
strncpy (buf, tail, sizeof(buf) - 1);
strncpy (tail, dns_prefix, sizeof(tail) - 1);
strncat (tail, domain, sizeof(tail) - 1 - strlen(tail));
strncat (tail, buf, sizeof(tail) - 1 - strlen(tail));
return ErrResolveLimit;
}
#ifndef __MINGW32__
// lookup using libresolv -lresolv
int ipfs_dnslink_resolv_lookupTXT(char ***txt, char *domain)
{
char buf[4096], *p;
int responseLength;
int i, l, n = 0;
ns_msg query_parse_msg;
ns_rr query_parse_rr;
u_char responseByte[4096];
// Use res_query from libresolv to retrieve TXT record from DNS server.
if ((responseLength = res_query(domain,ns_c_in,ns_t_txt,responseByte,sizeof(responseByte))) < 0 ||
ns_initparse(responseByte,responseLength,&query_parse_msg) < 0) {
return ErrResolveFailed;
} else {
l = sizeof (buf);
buf[--l] = '\0';
p = buf;
// save every TXT record to buffer separating with a \0
for (i=0 ; i < ns_msg_count(query_parse_msg,ns_s_an) ; i++) {
if (ns_parserr(&query_parse_msg,ns_s_an,i,&query_parse_rr)) {
return ErrResolveFailed;
} else {
const unsigned char *rdata = ns_rr_rdata(query_parse_rr);
memcpy(p, rdata+1, *rdata); // first byte is record length
p += *rdata; // update pointer
*p++ = '\0'; // mark end-of-record and update pointer to next record.
n++; // update record count
}
}
// allocate array for all records + NULL pointer terminator.
*txt = calloc(n+1, sizeof(void*));
if (!*txt) {
return ErrAllocFailed;
}
l = p - buf; // length of all records in buffer.
p = malloc(l); // allocate memory that will be used as string data at *txt array.
if (!p) {
free(*txt);
*txt = NULL;
return ErrAllocFailed;
}
memcpy(p, buf, l); // transfer from buffer to allocated memory.
for (i = 0 ; i < n ; i++) {
*txt[i] = p; // save position of current record at *txt array.
p = memchr(p, '\0', l - (p - *txt[0])) + 1; // find next record position after next \0
}
}
return 0;
}
#endif
// ipfs_dnslink_resolve_once implements resolver.
int ipfs_dnslink_resolve_once (char ***p, char *domain)
{
int err, i;
char **txt;
if (!p || !domain) {
return ErrInvalidParam;
}
*p = NULL;
if (!ipfs_isdomain_is_domain (domain)) {
return ErrInvalidDomain;
}
#ifndef __MINGW32__
if (!ipfs_dnslink_lookup_txt) { // if not set
ipfs_dnslink_lookup_txt = ipfs_dnslink_resolv_lookupTXT; // use default libresolv
}
err = ipfs_dnslink_lookup_txt (&txt, domain);
#endif
if (err) {
return err;
}
err = ErrResolveFailed;
for (i=0 ; txt[i] ; i++) {
err = ipfs_dnslink_parse_txt(*p, txt[i]);
if (!err) {
break;
}
}
free(*txt);
free(txt);
return err;
}
// ipfs_dnslink_parse_txt parses a TXT record value for a dnslink value.
// The TXT record must follow the dnslink format:
// TXT dnslink=<path>
// TXT dnslink=/foo/bar/baz
// ipfs_dnslink_parse_txt will return ErrInvalidDNSLink if parsing fails.
int ipfs_dnslink_parse_txt (char **path, char *txt)
{
char **parts;
if (!path || !txt) {
return ErrInvalidParam;
}
parts = ipfs_path_split_n (txt, "=", 2);
if (!parts) {
return ErrAllocFailed;
}
if (ipfs_path_segments_length (parts) == 2 && strcmp(parts[0], "dnslink")==0 && memcmp(parts[1], "/", 1)==0) {
*path = ipfs_path_clean_path(parts[1]);
if (path) {
ipfs_path_free_segments (&parts);
return 0;
}
}
ipfs_path_free_segments (&parts);
*path = NULL;
return ErrInvalidDNSLink;
}
// ipfs_dnslink_parse_link_domain parses a domain from a dnslink path.
// The link path must follow the dnslink format:
// /dns/<domain>/<path>
// /dns/ipfs.io
// /dns/ipfs.io/blog/0-hello-worlds
// ipfs_dnslink_parse_link_domain will return ErrInvalidDNSLink if parsing
// fails, and ErrInvalidDomain if the domain is not valid.
int ipfs_dnslink_parse_link_domain (char **domain, char**rest, char *txt)
{
char **parts;
int parts_len;
if (!domain || !rest || !txt) {
return ErrInvalidParam;
}
*domain = *rest = NULL;
parts = ipfs_path_split_n (txt, "/", 4);
parts_len = ipfs_path_segments_length(parts);
if (!parts || parts_len < 3 || parts[0][0]!='\0' || strcmp(parts[1], "dns") != 0) {
return ErrInvalidDNSLink;
}
if (! ipfs_isdomain_is_domain (parts[2])) {
ipfs_path_free_segments (&parts);
return ErrInvalidDomain;
}
*domain = malloc(strlen (parts[2]) + 1);
if (!*domain) {
ipfs_path_free_segments (&parts);
return ErrAllocFailed;
}
strcpy(*domain, parts[2]);
if (parts_len > 3) {
*rest = malloc(strlen (parts[3]) + 1);
if (!*rest) {
ipfs_path_free_segments (&parts);
free (*domain);
*domain = NULL;
return ErrAllocFailed;
}
strcpy(*rest, parts[3]);
}
return 0;
}

View file

@ -1,18 +0,0 @@
CC = gcc
CFLAGS = -O0 -I../include -I../c-libp2p/include -std=gnu99
ifdef DEBUG
CFLAGS += -g3
endif
DEPS =
OBJS =
%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
all: $(OBJS)
cd bitswap; make all;
clean:
rm -f *.o
cd bitswap; make clean;

View file

@ -1,18 +0,0 @@
CC = gcc
CFLAGS = -O0 -I../../include -I../../c-libp2p/include -I../../c-libp2p/c-multiaddr/include -I../../c-libp2p/c-multihash/include -I../../c-libp2p/c-protobuf -Wall -std=c99
ifdef DEBUG
CFLAGS += -g3
endif
LFLAGS =
DEPS =
OBJS = bitswap.o message.o network.o peer_request_queue.o want_manager.o wantlist_queue.o engine.o
%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
all: $(OBJS)
clean:
rm -f *.o

View file

@ -1,249 +0,0 @@
/**
* Methods for the Bitswap exchange
*/
#include <stdlib.h>
#include <unistd.h> // for sleep()
#include <pthread.h>
#include "libp2p/os/utils.h"
#include "libp2p/utils/logger.h"
#include "libp2p/net/stream.h"
#include "libp2p/net/connectionstream.h"
#include "ipfs/core/ipfs_node.h"
#include "ipfs/datastore/ds_helper.h"
#include "ipfs/exchange/exchange.h"
#include "ipfs/exchange/bitswap/bitswap.h"
#include "ipfs/exchange/bitswap/message.h"
#include "ipfs/exchange/bitswap/network.h"
#include "ipfs/exchange/bitswap/peer_request_queue.h"
#include "ipfs/exchange/bitswap/want_manager.h"
int ipfs_bitswap_can_handle(const struct StreamMessage* msg) {
if (msg == NULL || msg->data == NULL || msg->data_size == 0)
return 0;
char* result = strnstr((char*)msg->data, "/ipfs/bitswap", msg->data_size);
if(result == NULL || result != (char*)msg->data)
return 0;
return 1;
}
int ipfs_bitswap_shutdown_handler(void* context) {
return 1;
}
/***
* Handles the message
* @param incoming the incoming data buffer
* @param incoming_size the size of the incoming data buffer
* @param session_context the information about the incoming connection
* @param protocol_context the protocol-dependent context
* @returns 0 if the caller should not continue looping, <0 on error, >0 on success
*/
int ipfs_bitswap_handle_message(const struct StreamMessage* msg, struct Stream* stream, void* protocol_context) {
struct IpfsNode* local_node = (struct IpfsNode*)protocol_context;
struct SessionContext* session_context = libp2p_net_connection_get_session_context(stream);
if (session_context == NULL)
return -1;
int retVal = ipfs_bitswap_network_handle_message(local_node, session_context, msg->data, msg->data_size);
if (retVal == 0)
return -1;
return retVal;
}
struct Libp2pProtocolHandler* ipfs_bitswap_build_protocol_handler(const struct IpfsNode* local_node) {
struct Libp2pProtocolHandler* handler = (struct Libp2pProtocolHandler*) malloc(sizeof(struct Libp2pProtocolHandler));
if (handler != NULL) {
handler->context = (void*)local_node;
handler->CanHandle = ipfs_bitswap_can_handle;
handler->HandleMessage = ipfs_bitswap_handle_message;
handler->Shutdown = ipfs_bitswap_shutdown_handler;
}
return handler;
}
/**
* Create a new bitswap exchange
* @param sessionContext the context
* @returns an allocated Exchange structure
*/
struct Exchange* ipfs_bitswap_new(struct IpfsNode* ipfs_node) {
struct Exchange* exchange = (struct Exchange*) malloc(sizeof(struct Exchange));
if (exchange != NULL) {
struct BitswapContext* bitswapContext = (struct BitswapContext*) malloc(sizeof(struct BitswapContext));
if (bitswapContext == NULL) {
free(exchange);
return NULL;
}
bitswapContext->bitswap_engine = ipfs_bitswap_engine_new();
if (bitswapContext->bitswap_engine == NULL) {
free(bitswapContext);
free(exchange);
return NULL;
}
bitswapContext->localWantlist = ipfs_bitswap_wantlist_queue_new();
bitswapContext->peerRequestQueue = ipfs_bitswap_peer_request_queue_new();
bitswapContext->ipfsNode = ipfs_node;
exchange->exchangeContext = (void*) bitswapContext;
exchange->IsOnline = ipfs_bitswap_is_online;
exchange->Close = ipfs_bitswap_close;
exchange->HasBlock = ipfs_bitswap_has_block;
exchange->GetBlock = ipfs_bitswap_get_block;
exchange->GetBlockAsync = ipfs_bitswap_get_block_async;
exchange->GetBlocks = ipfs_bitswap_get_blocks;
// Start the threads for the network
ipfs_bitswap_engine_start(bitswapContext);
libp2p_logger_debug("bitswap", "Bitswap engine started\n");
}
return exchange;
}
/**
* Clean up resources within an Exchange struct
* @param exchange the exchange to free
* @returns true(1)
*/
int ipfs_bitswap_free(struct Exchange* exchange) {
if (exchange != NULL) {
if (exchange->exchangeContext != NULL) {
struct BitswapContext* bitswapContext = (struct BitswapContext*) exchange->exchangeContext;
if (bitswapContext != NULL)
ipfs_bitswap_engine_stop(bitswapContext);
if (bitswapContext->localWantlist != NULL) {
ipfs_bitswap_wantlist_queue_free(bitswapContext->localWantlist);
bitswapContext->localWantlist = NULL;
}
if (bitswapContext->peerRequestQueue != NULL) {
ipfs_bitswap_peer_request_queue_free(bitswapContext->peerRequestQueue);
bitswapContext->peerRequestQueue = NULL;
}
free(exchange->exchangeContext);
}
free(exchange);
}
return 1;
}
/**
* Implements the Exchange->IsOnline method
*/
int ipfs_bitswap_is_online(struct Exchange* exchange) {
return 1;
}
/***
* Implements the Exchange->Close method
*/
int ipfs_bitswap_close(struct Exchange* exchange) {
ipfs_bitswap_free(exchange);
return 0;
}
/**
* Implements the Exchange->HasBlock method
* Some notes from the GO version say that this is normally called by user
* interaction (i.e. user added a file).
* But this does not make sense right now, as the GO code looks like it
* adds the block to the blockstore. This still has to be sorted.
*/
int ipfs_bitswap_has_block(struct Exchange* exchange, struct Block* block) {
// add the block to the blockstore
struct BitswapContext* context = exchange->exchangeContext;
size_t bytes_written;
context->ipfsNode->blockstore->Put(context->ipfsNode->blockstore->blockstoreContext, block, &bytes_written);
// add it to the datastore
ipfs_datastore_helper_add_block_to_datastore(block, context->ipfsNode->repo->config->datastore);
// update requests
struct WantListQueueEntry* queueEntry = ipfs_bitswap_wantlist_queue_find(context->localWantlist, block->cid);
if (queueEntry != NULL) {
queueEntry->block = block;
}
// TODO: Announce to world that we now have the block
return 0;
}
/**
* Implements the Exchange->GetBlock method
* We're asking for this method to get the block from peers. Perhaps this should be
* taking in a pointer to a callback, as this could take a while (or fail).
* @param exchangeContext a BitswapContext
* @param cid the Cid to look for
* @param block a pointer to where to put the result
* @returns true(1) if found, false(0) if not
*/
int ipfs_bitswap_get_block(struct Exchange* exchange, struct Cid* cid, struct Block** block) {
struct BitswapContext* bitswapContext = (struct BitswapContext*)exchange->exchangeContext;
if (bitswapContext != NULL) {
// check locally first
if (bitswapContext->ipfsNode->blockstore->Get(bitswapContext->ipfsNode->blockstore->blockstoreContext, cid, block))
return 1;
// now ask the network
//NOTE: this timeout should be configurable
int timeout = 60;
int waitSecs = 1;
int timeTaken = 0;
struct WantListSession *wantlist_session = ipfs_bitswap_wantlist_session_new();
wantlist_session->type = WANTLIST_SESSION_TYPE_LOCAL;
wantlist_session->context = (void*)bitswapContext->ipfsNode;
struct WantListQueueEntry* want_entry = ipfs_bitswap_want_manager_add(bitswapContext, cid, wantlist_session);
if (want_entry != NULL) {
// loop waiting for it to fill
while(1) {
if (want_entry->block != NULL) {
*block = ipfs_block_copy(want_entry->block);
// error or not, we no longer need the block (decrement reference count)
ipfs_bitswap_want_manager_remove(bitswapContext, cid);
if (*block == NULL) {
return 0;
}
return 1;
}
//TODO: This is a busy-loop. Find another way.
timeTaken += waitSecs;
if (timeTaken >= timeout) {
// It took too long. Stop looking.
ipfs_bitswap_want_manager_remove(bitswapContext, cid);
break;
}
sleep(waitSecs);
}
}
}
return 0;
}
/**
* Implements the Exchange->GetBlock method
* We're asking for this method to get the block from peers. Perhaps this should be
* taking in a pointer to a callback, as this could take a while (or fail).
* @param exchangeContext a BitswapContext
* @param cid the Cid to look for
* @param block a pointer to where to put the result
* @returns true(1) if found, false(0) if not
*/
int ipfs_bitswap_get_block_async(struct Exchange* exchange, struct Cid* cid, struct Block** block) {
struct BitswapContext* bitswapContext = (struct BitswapContext*)exchange->exchangeContext;
if (bitswapContext != NULL) {
// check locally first
struct Block* block;
if (bitswapContext->ipfsNode->blockstore->Get(bitswapContext->ipfsNode->blockstore->blockstoreContext, cid, &block)) {
return 1;
}
// now ask the network
struct WantListSession* wantlist_session = ipfs_bitswap_wantlist_session_new();
wantlist_session->type = WANTLIST_SESSION_TYPE_LOCAL;
wantlist_session->context = (void*)bitswapContext->ipfsNode;
ipfs_bitswap_want_manager_add(bitswapContext, cid, wantlist_session);
// TODO: return something that they can watch
return 1;
}
return 0;
}
/**
* Implements the Exchange->GetBlocks method
*/
int ipfs_bitswap_get_blocks(struct Exchange* exchange, struct Libp2pVector* Cids, struct Libp2pVector** blocks) {
// TODO: Implement this method
return 0;
}

View file

@ -1,198 +0,0 @@
#include <unistd.h>
#include <pthread.h>
#include "libp2p/utils/logger.h"
#include "ipfs/core/null.h"
#include "ipfs/exchange/bitswap/engine.h"
#include "ipfs/exchange/bitswap/wantlist_queue.h"
#include "ipfs/exchange/bitswap/peer_request_queue.h"
/***
* Implementation of the bitswap engine
*/
/***
* Allocate resources for a BitswapEngine
* @returns a new struct BitswapEngine
*/
struct BitswapEngine* ipfs_bitswap_engine_new() {
struct BitswapEngine* engine = (struct BitswapEngine*) malloc(sizeof(struct BitswapEngine));
if (engine != NULL) {
engine->shutting_down = 0;
}
return engine;
}
/***
* Deallocate resources from struct BitswapEngine
* @param engine the engine to free
* @returns true(1)
*/
int ipfs_bitswap_engine_free(struct BitswapEngine* engine) {
free(engine);
return 1;
}
/***
* A separate thread that processes the queue of local requests
* @param context the context
*/
void* ipfs_bitswap_engine_wantlist_processor_start(void* ctx) {
struct BitswapContext* context = (struct BitswapContext*)ctx;
// the loop
while (!context->bitswap_engine->shutting_down) {
struct WantListQueueEntry* item = ipfs_bitswap_wantlist_queue_pop(context->localWantlist);
if (item != NULL) {
if (item->attempts > 10) {
// we have tried too many times
for (int i = 0; i < item->sessionsRequesting->total; i++) {
struct WantListSession* curr_session = (struct WantListSession*) libp2p_utils_vector_get(item->sessionsRequesting, i);
ipfs_bitswap_wantlist_queue_remove(context->localWantlist, item->cid, curr_session);
}
} else {
// if there is something on the queue process it.
ipfs_bitswap_wantlist_process_entry(context, item);
}
} else {
// if there is nothing on the queue, wait...
sleep(2);
}
}
return NULL;
}
/***
* A separate thread that processes the queue of remote requests
* @param context the context
*/
void* ipfs_bitswap_engine_peer_request_processor_start(void* ctx) {
struct BitswapContext* context = (struct BitswapContext*)ctx;
// the loop
struct Libp2pLinkedList* current = context->ipfsNode->peerstore->head_entry;
int did_some_processing = 0;
while (1) {
if (context->bitswap_engine->shutting_down) // system shutting down
break;
if (current == NULL) { // the PeerStore is empty
libp2p_logger_debug("bitswap_engine", "Peerstore is empty. Pausing.\n");
sleep(1);
continue;
}
if (current->item == NULL) {
// error
libp2p_logger_error("bitswap_engine", "Peerstore has a null entry.\n");
break;
}
// see if they want something
struct Libp2pPeer* current_peer_entry = ((struct PeerEntry*)current->item)->peer;
if (current_peer_entry == NULL) {
// error
libp2p_logger_error("bitswap_engine", "Peerstore has an item that is a null peer.\n");
break;
}
if (current_peer_entry->connection_type == CONNECTION_TYPE_CONNECTED) {
if (current_peer_entry->sessionContext == NULL || current_peer_entry->sessionContext->default_stream == NULL) {
current_peer_entry->connection_type = CONNECTION_TYPE_NOT_CONNECTED;
} else {
// check the network to see if there is anything waiting for us (if the stream is idle)
if (libp2p_stream_try_lock(current_peer_entry->sessionContext->default_stream)) {
int retVal = current_peer_entry->sessionContext->default_stream->peek(current_peer_entry->sessionContext);
if (retVal < 0) {
libp2p_logger_debug("bitswap_engine", "We thought we were connected, but Peek reported an error.\n");
libp2p_peer_handle_connection_error(current_peer_entry);
} else if (retVal > 0) {
libp2p_logger_debug("bitswap_engine", "%d bytes waiting on network for peer %s.\n", retVal, libp2p_peer_id_to_string(current_peer_entry));
struct StreamMessage* buffer = NULL;
if (current_peer_entry->sessionContext->default_stream->read(current_peer_entry->sessionContext, &buffer, 1)) {
// handle it
libp2p_logger_debug("bitswap_engine", "%lu bytes read, result: [%s].\n", buffer->data_size, buffer->data);
int retVal = libp2p_protocol_marshal(buffer, current_peer_entry->sessionContext->default_stream, context->ipfsNode->protocol_handlers);
libp2p_stream_message_free(buffer);
did_some_processing = 1;
if (retVal == -1) {
libp2p_logger_error("bitswap_engine", "protocol_marshal tried to handle the network traffic, but failed.\n");
// there was a problem. Clean up
libp2p_peer_handle_connection_error(current_peer_entry);
}
} else {
libp2p_logger_error("bitswap_engine", "It was said that there was %d bytes to read, but there wasn't. Cleaning up connection.\n");
libp2p_peer_handle_connection_error(current_peer_entry);
}
}
libp2p_stream_unlock(current_peer_entry->sessionContext->default_stream);
}
}
} else {
if (current_peer_entry->is_local) {
//libp2p_logger_debug("bitswap_engine", "Local peer %s. Skipping.\n", current_peer_entry->id);
} else {
//libp2p_logger_debug("bitswap_engine", "We are not connected to this peer %s.\n", current_peer_entry->id);
}
}
// attempt to get queue and process
struct PeerRequestEntry* entry = ipfs_bitswap_peer_request_queue_find_entry(context->peerRequestQueue, current_peer_entry);
if (entry != NULL) {
//libp2p_logger_debug("bitswap_engine", "Processing queue for peer %s.\n", current_peer_entry->id);
// we have a queue. Do some queue processing
struct PeerRequest* item = entry->current;
if (item != NULL) {
// if there is something on the queue process it.
if (ipfs_bitswap_peer_request_process_entry(context, item))
did_some_processing = 1;
}
}
// get next peer (or reset to head entry)
if (current->next == NULL) {
current = context->ipfsNode->peerstore->head_entry;
if (!did_some_processing) {
// we did nothing in this run through the peerstore. sleep for a sec
sleep(1);
}
did_some_processing = 0;
}
else {
//libp2p_logger_debug("bitswap_engine", "Moving on to the next peer.\n");
current = current->next;
}
}
return NULL;
}
/**
* Starts the bitswap engine that processes queue items. There
* should only be one of these per ipfs instance.
*
* @param context the context
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_bitswap_engine_start(const struct BitswapContext* context) {
context->bitswap_engine->shutting_down = 0;
// fire off the threads
if (pthread_create(&context->bitswap_engine->wantlist_processor_thread, NULL, ipfs_bitswap_engine_wantlist_processor_start, (void*)context)) {
return 0;
}
if (pthread_create(&context->bitswap_engine->peer_request_processor_thread, NULL, ipfs_bitswap_engine_peer_request_processor_start, (void*)context)) {
ipfs_bitswap_engine_stop(context);
return 0;
}
return 1;
}
/***
* Shut down the engine
* @param context the context
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_bitswap_engine_stop(const struct BitswapContext* context) {
context->bitswap_engine->shutting_down = 1;
int error1 = pthread_join(context->bitswap_engine->wantlist_processor_thread, NULL);
int error2 = pthread_join(context->bitswap_engine->peer_request_processor_thread, NULL);
ipfs_bitswap_engine_free(context->bitswap_engine);
return !error1 && !error2;
}

View file

@ -1,742 +0,0 @@
#include <stdlib.h>
#include "protobuf.h"
#include "varint.h"
#include "libp2p/utils/vector.h"
#include "ipfs/blocks/block.h"
#include "ipfs/exchange/bitswap/message.h"
#include "ipfs/exchange/bitswap/peer_request_queue.h"
/***
* Allocate memory for a struct BitswapBlock
* @returns a new BitswapBlock
*/
struct BitswapBlock* ipfs_bitswap_block_new() {
struct BitswapBlock* block = (struct BitswapBlock*) malloc(sizeof(struct BitswapBlock));
if (block != NULL) {
block->bytes_size = 0;
block->bytes = NULL;
block->prefix_size = 0;
block->prefix = NULL;
}
return block;
}
/**
* Deallocate memory for a struct BitswapBlock
* @param block the block to deallocate
* @returns true(1)
*/
int ipfs_bitswap_block_free(struct BitswapBlock* block) {
if (block != NULL) {
if (block->bytes != NULL)
free(block->bytes);
if (block->prefix != NULL)
free(block->prefix);
free(block);
}
return 1;
}
/**
* Retrieve an estimate of the size of a protobuf'd BitswapBlock
* @returns the approximate (maximum actually) size of a protobuf'd BitswapBlock
*/
size_t ipfs_bitswap_message_block_protobuf_size(struct BitswapBlock* block) {
// protobuf prefix + prefix + bytes = 33 + array sizes
return 33 + block->prefix_size + block->bytes_size;
}
/***
* Encode a BitswapBlock
* @param incoming the block to encode
* @param outgoing where to place the results
* @param max_size the maximum allocated space for outgoing
* @param bytes_written the number of bytes written to outgoing
*/
int ipfs_bitswap_message_block_protobuf_encode(struct BitswapBlock* incoming, uint8_t* outgoing, size_t max_size, size_t* bytes_written) {
// 2 WIRETYPE_LENGTH_DELIMITED fields of prefix and bytes
size_t bytes_used;
*bytes_written = 0;
if (incoming != NULL) {
if (!protobuf_encode_length_delimited(1, WIRETYPE_LENGTH_DELIMITED, (char*)incoming->prefix, incoming->prefix_size, outgoing, max_size, &bytes_used))
return 0;
*bytes_written += bytes_used;
if (!protobuf_encode_length_delimited(2, WIRETYPE_LENGTH_DELIMITED, (char*)incoming->bytes, incoming->bytes_size, &outgoing[*bytes_written], max_size - (*bytes_written), &bytes_used))
return 0;
*bytes_written += bytes_used;
}
return 1;
}
/***
* Decode a protobuf to a BitswapBlock
* @param buffer the incoming protobuf
* @param buffer_length the length of the incoming protobuf buffer
* @param output a pointer to the BitswapBlock that will be allocated
* @returns true(1) on success, false(0) if not. If false, any memory was deallocated
*/
int ipfs_bitswap_message_block_protobuf_decode(uint8_t* buffer, size_t buffer_length, struct BitswapBlock** output) {
size_t pos = 0;
int retVal = 0;
*output = NULL;
// short cut for nulls
if (buffer_length == 0)
return 1;
*output = (struct BitswapBlock*) malloc(sizeof(struct BitswapBlock));
if (*output == NULL)
goto exit;
struct BitswapBlock* block = *output;
while(pos < buffer_length) {
size_t bytes_read = 0;
int field_no;
enum WireType field_type;
if (protobuf_decode_field_and_type(&buffer[pos], buffer_length, &field_no, &field_type, &bytes_read) == 0) {
goto exit;
}
pos += bytes_read;
switch(field_no) {
case (1):
if (!protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&block->prefix, &block->prefix_size, &bytes_read))
goto exit;
break;
case (2):
if (!protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&block->bytes, &block->bytes_size, &bytes_read))
goto exit;
break;
}
}
retVal = 1;
exit:
if (retVal == 0) {
if (*output != NULL)
free(*output);
*output = NULL;
}
return retVal;
}
/***
* Allocate memory for a new WantlistEntry
* @returns the newly allocated WantlistEntry
*/
struct WantlistEntry* ipfs_bitswap_wantlist_entry_new() {
struct WantlistEntry* entry = (struct WantlistEntry*) malloc(sizeof(struct WantlistEntry));
if (entry == NULL)
return NULL;
entry->block = NULL;
entry->block_size = 0;
entry->cancel = 0;
entry->priority = 1;
return entry;
}
/***
* Free allocations of a WantlistEntry
* @param entry the WantlistEntry
* @returns true(1)
*/
int ipfs_bitswap_wantlist_entry_free(struct WantlistEntry* entry) {
if (entry != NULL) {
if (entry->block != NULL)
free(entry->block);
free(entry);
entry = NULL;
}
return 1;
}
/**
* Retrieve an estimate of the size of a protobuf'd WantlistEntry
* @param entry the struct to examine
* @returns the approximate (maximum actually) size of a protobuf'd WantlistEntry
*/
size_t ipfs_bitswap_wantlist_entry_protobuf_encode_size(struct WantlistEntry* entry) {
// protobuf prefix + block + cancel + priority
return 33 + entry->block_size;
}
/***
* Encode a WantlistEntry into a Protobuf
* @param entry the WantlistEntry to encode
* @param buffer where to put the results
* @param buffer_length the maximum size of the buffer
* @param bytes_written the number of bytes written into the buffer
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_bitswap_wantlist_entry_protobuf_encode(struct WantlistEntry* entry, unsigned char* buffer, size_t buffer_length, size_t* bytes_written) {
size_t bytes_used;
*bytes_written = 0;
if (entry != NULL) {
if (!protobuf_encode_length_delimited(1, WIRETYPE_LENGTH_DELIMITED, (char*)entry->block, entry->block_size, &buffer[*bytes_written], buffer_length - (*bytes_written), &bytes_used))
return 0;
*bytes_written += bytes_used;
if (!protobuf_encode_varint(2, WIRETYPE_VARINT, entry->cancel, &buffer[*bytes_written], buffer_length - (*bytes_written), &bytes_used))
return 0;
*bytes_written += bytes_used;
if (!protobuf_encode_varint(3, WIRETYPE_VARINT, entry->priority, &buffer[*bytes_written], buffer_length - (*bytes_written), &bytes_used))
return 0;
*bytes_written += bytes_used;
}
return 1;
}
/***
* Decode a protobuf into a struct WantlistEntry
* @param buffer the protobuf buffer
* @param buffer_length the length of the data in the protobuf buffer
* @param output the resultant WantlistEntry
* @returns true(1) on success, otherwise false(0)
*/
int ipfs_bitswap_wantlist_entry_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct WantlistEntry** output) {
size_t pos = 0;
int retVal = 0;
struct WantlistEntry* entry = NULL;
*output = NULL;
// short cut for nulls
if (buffer_length == 0)
return 1;
*output = (struct WantlistEntry*) malloc(sizeof(struct WantlistEntry));
if (*output == NULL)
goto exit;
entry = *output;
while(pos < buffer_length) {
size_t bytes_read = 0;
int field_no;
enum WireType field_type;
if (protobuf_decode_field_and_type(&buffer[pos], buffer_length, &field_no, &field_type, &bytes_read) == 0) {
goto exit;
}
pos += bytes_read;
switch(field_no) {
case (1):
if (!protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&entry->block, &entry->block_size, &bytes_read))
goto exit;
pos += bytes_read;
break;
case (2):
entry->cancel = varint_decode(&buffer[pos], buffer_length - pos, &bytes_read);
pos += bytes_read;
break;
case (3):
entry->priority = varint_decode(&buffer[pos], buffer_length - pos, &bytes_read);
pos += bytes_read;
break;
}
}
retVal = 1;
exit:
if (retVal == 0) {
if (entry != NULL)
free(entry);
*output = NULL;
}
return retVal;
}
/***
* Allocate memory for a new Bitswap Message WantList
* @returns the allocated struct BitswapWantlist
*/
struct BitswapWantlist* ipfs_bitswap_wantlist_new() {
struct BitswapWantlist* list = (struct BitswapWantlist*) malloc(sizeof(struct BitswapWantlist));
if (list != NULL) {
list->entries = NULL;
list->full = 1;
}
return list;
}
/**
* Free the resources used by a Wantlist
* @param list the list to free
* @returns true(1)
*/
int ipfs_bitswap_wantlist_free(struct BitswapWantlist* list) {
if (list != NULL) {
if (list->entries != NULL) {
for(int i = 0; i < list->entries->total; i++) {
// free each item in the vector
struct WantlistEntry* entry = (struct WantlistEntry*) libp2p_utils_vector_get(list->entries, i);
ipfs_bitswap_wantlist_entry_free(entry);
}
libp2p_utils_vector_free(list->entries);
}
free(list);
}
return 1;
}
/***
* Calculate the maximum size of a protobuf'd BitswapWantlist
* @param list the Wantlist
* @returns the maximum size of the protobuf'd list
*/
size_t ipfs_bitswap_wantlist_protobuf_encode_size(struct BitswapWantlist* list) {
size_t total = 0;
if (list != NULL) {
for(int i = 0; i < list->entries->total; i++) {
struct WantlistEntry* entry = (struct WantlistEntry*) libp2p_utils_vector_get(list->entries, i);
total += ipfs_bitswap_wantlist_entry_protobuf_encode_size(entry);
}
total += 11 + 12 + 11;
}
return total;
}
/***
* Encode a BitswapWantlist into a protobuf buffer
* @param list the list to encode
* @param buffer the buffer to fill
* @param buffer_length the length of the allocated buffer
* @param bytes_written the total number of bytes written to the buffer
* @returns true(1) on success, otherwise false(0)
*/
int ipfs_bitswap_wantlist_protobuf_encode(struct BitswapWantlist* list, unsigned char* buffer, size_t buffer_length, size_t* bytes_written) {
size_t bytes_used = 0;
*bytes_written = 0;
if (list != NULL) {
// the vector of entries
for(int i = 0; i < list->entries->total; i++) {
struct WantlistEntry* entry = (struct WantlistEntry*) libp2p_utils_vector_get(list->entries, i);
// protobuf the entry
size_t temp_buffer_size = ipfs_bitswap_wantlist_entry_protobuf_encode_size(entry);
uint8_t* temp_buffer = (uint8_t*) malloc(temp_buffer_size);
if (temp_buffer == NULL)
return 0;
if (!ipfs_bitswap_wantlist_entry_protobuf_encode(entry, temp_buffer, temp_buffer_size, &temp_buffer_size)) {
free(temp_buffer);
return 0;
}
// we've got the protobuf'd entry, now put it in the real buffer
if (!protobuf_encode_length_delimited(1, WIRETYPE_LENGTH_DELIMITED, (char*)temp_buffer, temp_buffer_size, &buffer[*bytes_written], buffer_length - (*bytes_written), &bytes_used)) {
free(temp_buffer);
return 0;
}
// all went okay. Clean up and do it again...
free(temp_buffer);
*bytes_written += bytes_used;
}
// if this is the full list or not...
if (!protobuf_encode_varint(2, WIRETYPE_VARINT, list->full, &buffer[*bytes_written], buffer_length - (*bytes_written), &bytes_used))
return 0;
*bytes_written += bytes_used;
}
return 1;
}
/***
* Decode a Wantlist from a protobuf
* @param buffer the protobuf
* @param buffer_length the length of the protobuf
* @param output the newly allocated BitswapWantlist
* @returns true(1) on success, otherwise false(0)
*/
int ipfs_bitswap_wantlist_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct BitswapWantlist** output) {
size_t pos = 0;
*output = NULL;
// short cut for nulls
if (buffer_length == 0)
return 1;
*output = ipfs_bitswap_wantlist_new();
if (*output == NULL)
return 0;
struct BitswapWantlist* list = *output;
while(pos < buffer_length) {
size_t bytes_read = 0;
int field_no;
enum WireType field_type;
if (protobuf_decode_field_and_type(&buffer[pos], buffer_length, &field_no, &field_type, &bytes_read) == 0) {
return 0;
}
pos += bytes_read;
switch(field_no) {
case (1): {
// a WantlistEntry
size_t temp_size = 0;
uint8_t* temp = NULL;
if (!protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&temp, &temp_size, &bytes_read)) {
return 0;
}
struct WantlistEntry* entry = NULL;
if (!ipfs_bitswap_wantlist_entry_protobuf_decode(temp, temp_size, &entry)) {
free(temp);
return 0;
}
free(temp);
if (list->entries == NULL) {
list->entries = libp2p_utils_vector_new(1);
}
libp2p_utils_vector_add(list->entries, (void*)entry);
pos += bytes_read;
break;
}
case (2): {
list->full = varint_decode(&buffer[pos], buffer_length - pos, &bytes_read);
pos += bytes_read;
break;
}
}
}
return 1;
}
/***
* Bitswap Message
*
*/
/***
* Allocate memory for a new Bitswap Message
* @returns the allocated struct BitswapMessage
*/
struct BitswapMessage* ipfs_bitswap_message_new() {
struct BitswapMessage* message = (struct BitswapMessage*) malloc(sizeof(struct BitswapMessage));
if (message != NULL) {
message->blocks = NULL;
message->payload = NULL;
message->wantlist = NULL;
}
return message;
}
/**
* Free the resources used by a BitswapMessage
* @param message the BitswapMessage to free
* @returns true(1)
*/
int ipfs_bitswap_message_free(struct BitswapMessage* message) {
if (message != NULL) {
if (message->blocks != NULL) {
// blocks are just byte arrays in bitswap 1.0.0, so throw it in a struct
// so it can be put in a vector
for(int i = 0; i < message->blocks->total; i++) {
// free each item in the vector
struct Block* entry = (struct Block*) libp2p_utils_vector_get(message->blocks, i);
ipfs_block_free(entry);
}
libp2p_utils_vector_free(message->blocks);
}
if (message->payload != NULL) {
for(int i = 0; i < message->payload->total; i++) {
// free each item in the vector
struct Block* entry = (struct Block*) libp2p_utils_vector_get(message->payload, i);
ipfs_block_free(entry);
}
libp2p_utils_vector_free(message->payload);
}
if (message->wantlist != NULL) {
ipfs_bitswap_wantlist_free(message->wantlist);
}
free(message);
}
return 1;
}
/***
* Calculate the maximum size of a protobuf'd BitswapMessage
* @param message the BitswapMessage
* @returns the maximum size of the protobuf'd BitswapMessage
*/
size_t ipfs_bitswap_message_protobuf_encode_size(const struct BitswapMessage* message) {
size_t total = 0;
if (message != NULL) {
if (message->blocks != NULL) {
for(int i = 0; i < message->blocks->total; i++) {
struct Block* entry = (struct Block*) libp2p_utils_vector_get(message->blocks, i);
total += 11 + entry->data_length;
}
}
if (message->payload != NULL) {
for(int i = 0; i < message->payload->total; i++) {
struct Block* entry = (struct Block*) libp2p_utils_vector_get(message->payload, i);
total += 11 + ipfs_blocks_block_protobuf_encode_size(entry);
}
}
if (message->wantlist != NULL) {
total += ipfs_bitswap_wantlist_protobuf_encode_size(message->wantlist);
}
total += 11 + 12 + 11;
}
return total;
}
/***
* Encode a BitswapMessage into a protobuf buffer
* @param message the message to encode
* @param buffer the buffer to fill
* @param buffer_length the length of the allocated buffer
* @param bytes_written the total number of bytes written to the buffer
* @returns true(1) on success, otherwise false(0)
*/
int ipfs_bitswap_message_protobuf_encode(const struct BitswapMessage* message, unsigned char* buffer, size_t buffer_length, size_t* bytes_written) {
size_t bytes_used = 0;
*bytes_written = 0;
if (message != NULL) {
// the vector of blocks that are actually to be turned back into byte arrays
if (message->blocks != NULL) {
for(int i = 0; i < message->blocks->total; i++) {
struct Block* entry = (struct Block*) libp2p_utils_vector_get(message->blocks, i);
// blocks are just variable length byte streams
if (!protobuf_encode_length_delimited(1, WIRETYPE_LENGTH_DELIMITED, (char*)entry->data, entry->data_length, &buffer[*bytes_written], buffer_length - (*bytes_written), &bytes_used)) {
return 0;
}
*bytes_written += bytes_used;
}
}
// the vector of Blocks that are actually blocks
if (message->payload != NULL) {
for(int i = 0; i < message->payload->total; i++) {
struct Block* entry = (struct Block*) libp2p_utils_vector_get(message->payload, i);
// protobuf it
size_t temp_size = ipfs_blocks_block_protobuf_encode_size(entry);
uint8_t* temp = (uint8_t*) malloc(temp_size);
if (temp == NULL) {
// memory issues
return 0;
}
if (!ipfs_blocks_block_protobuf_encode(entry, temp, temp_size, &temp_size)) {
free(temp);
return 0;
}
// put it in the buffer
if (!protobuf_encode_length_delimited(2, WIRETYPE_LENGTH_DELIMITED, (char*)temp, temp_size, &buffer[*bytes_written], buffer_length - (*bytes_written), &bytes_used)) {
free(temp);
return 0;
}
*bytes_written += bytes_used;
free(temp);
}
}
// the WantList
if (message->wantlist != NULL) {
size_t temp_size = ipfs_bitswap_wantlist_protobuf_encode_size(message->wantlist);
uint8_t* temp = (uint8_t*) malloc(temp_size);
if (temp == NULL) {
return 0;
}
if (!ipfs_bitswap_wantlist_protobuf_encode(message->wantlist, temp, temp_size, &temp_size)) {
free(temp);
return 0;
}
if (!protobuf_encode_length_delimited(3, WIRETYPE_LENGTH_DELIMITED, (char*)temp, temp_size, &buffer[*bytes_written], buffer_length - (*bytes_written), &bytes_used)) {
free(temp);
return 0;
}
*bytes_written += bytes_used;
free(temp);
}
}
return 1;
}
/***
* Decode a BitswapMessage from a protobuf
* @param buffer the protobuf
* @param buffer_length the length of the protobuf
* @param output the newly allocated BitswapMessage
* @returns true(1) on success, otherwise false(0)
*/
int ipfs_bitswap_message_protobuf_decode(const uint8_t* buffer, size_t buffer_length, struct BitswapMessage** output) {
size_t pos = 0;
*output = NULL;
// short cut for nulls
if (buffer_length == 0)
return 1;
*output = (struct BitswapMessage*) ipfs_bitswap_message_new();
if (*output == NULL)
return 0;
struct BitswapMessage* message = *output;
while(pos < buffer_length) {
size_t bytes_read = 0;
int field_no;
enum WireType field_type;
if (protobuf_decode_field_and_type(&buffer[pos], buffer_length, &field_no, &field_type, &bytes_read) == 0) {
return 0;
}
pos += bytes_read;
switch(field_no) {
case (1): {
// a Blocks entry that is just an array of bytes
struct Block* temp = ipfs_block_new();
if (!protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&temp->data, &temp->data_length, &bytes_read)) {
return 0;
}
if (message->blocks == NULL) {
message->blocks = libp2p_utils_vector_new(1);
}
libp2p_utils_vector_add(message->blocks, (void*)temp);
pos += bytes_read;
break;
}
case (2): {
// a block entry that is a real block struct
size_t temp_size = 0;
uint8_t* temp = NULL;
if (!protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&temp, &temp_size, &bytes_read)) {
return 0;
}
// we have the bytes, turn it into a Block struct
struct Block* block = NULL;
if (!ipfs_blocks_block_protobuf_decode(temp, temp_size, &block)) {
free(temp);
return 0;
}
free(temp);
if (message->payload == NULL) {
message->payload = libp2p_utils_vector_new(1);
}
libp2p_utils_vector_add(message->payload, (void*)block);
pos += bytes_read;
break;
}
case(3): {
// a Wantlist
size_t temp_size = 0;
uint8_t* temp = NULL;
if (!protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&temp, &temp_size, &bytes_read)) {
return 0;
}
// we have the protobuf'd wantlist, now turn it into a Wantlist struct.
if (!ipfs_bitswap_wantlist_protobuf_decode(temp, temp_size, &message->wantlist)) {
free(temp);
return 0;
}
free(temp);
pos += bytes_read;
break;
}
}
}
return 1;
}
/****
* Add a vector of Cids to the bitswap message
* @param message the message
* @param cids a Libp2pVector of cids
* @returns true(1) on success, otherwise false(0)
*/
int ipfs_bitswap_message_add_wantlist_items(struct BitswapMessage* message, struct Libp2pVector* cids) {
if (message->wantlist == NULL) {
message->wantlist = ipfs_bitswap_wantlist_new();
if (message->wantlist == NULL)
return 0;
}
if (message->wantlist->entries == NULL) {
message->wantlist->entries = libp2p_utils_vector_new(1);
if (message->wantlist->entries == NULL)
return 0;
}
for(int i = 0; i < cids->total; i++) {
struct CidEntry* cidEntry = (struct CidEntry*)libp2p_utils_vector_get(cids, i);
if (cidEntry->cancel && cidEntry->cancel_has_been_sent)
continue;
if (!cidEntry->cancel && cidEntry->request_has_been_sent)
continue;
struct WantlistEntry* entry = ipfs_bitswap_wantlist_entry_new();
entry->block_size = ipfs_cid_protobuf_encode_size(cidEntry->cid);
entry->block = (unsigned char*) malloc(entry->block_size);
if (entry->block == NULL) {
return 0;
}
if (!ipfs_cid_protobuf_encode(cidEntry->cid, entry->block, entry->block_size, &entry->block_size)) {
// TODO: we should do more than return a half-baked list
return 0;
}
entry->cancel = cidEntry->cancel;
entry->priority = 1;
libp2p_utils_vector_add(message->wantlist->entries, entry);
if (cidEntry->cancel)
cidEntry->cancel_has_been_sent = 1;
else
cidEntry->request_has_been_sent = 1;
}
return 1;
}
/***
* Look through vector for specific Cid, then mark it cancel
* @param vector the vector of CidEntrys
* @param incoming_cid the cid to look for
* @returns true(1) if found one, false(0) if not
*/
int ipfs_bitswap_message_cancel_cid(struct Libp2pVector* vector, struct Cid* incoming_cid) {
for(int i = 0; i < vector->total; i++) {
struct CidEntry* entry = (struct CidEntry*)libp2p_utils_vector_get(vector, i);
if (ipfs_cid_compare(entry->cid, incoming_cid) == 0) {
entry->cancel = 1;
return 1;
}
}
return 0;
}
/***
* Add the blocks to the BitswapMessage
* @param message the message
* @param blocks the requested blocks
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_bitswap_message_add_blocks(struct BitswapMessage* message, struct Libp2pVector* blocks, struct Libp2pVector* cids_they_want) {
// bitswap 1.0 uses blocks, bitswap 1.1 uses payload
if (message == NULL)
return 0;
if (blocks == NULL || blocks->total == 0)
return 0;
if (message->payload == NULL) {
message->payload = libp2p_utils_vector_new(blocks->total);
if (message->payload == NULL)
return 0;
}
int tot_blocks = blocks->total;
for(int i = 0; i < tot_blocks; i++) {
const struct Block* current = (const struct Block*) libp2p_utils_vector_get(blocks, i);
libp2p_utils_vector_add(message->payload, current);
ipfs_bitswap_message_cancel_cid(cids_they_want, current->cid);
}
for (int i = 0; i < tot_blocks; i++) {
libp2p_utils_vector_delete(blocks, 0);
}
return 1;
}

View file

@ -1,146 +0,0 @@
/***
* This implements the BitswapNetwork. Members of this network can fill requests and
* smartly handle queues of local and remote requests.
*
* For a somewhat accurate diagram of how this may work, @see https://github.com/ipfs/js-ipfs-bitswap
*/
#include <pthread.h>
#include "libp2p/utils/logger.h"
#include "ipfs/exchange/bitswap/network.h"
#include "ipfs/exchange/bitswap/peer_request_queue.h"
/****
* send a message to a particular peer
* @param context the BitswapContext
* @param peer the peer that is the recipient
* @param message the message to send
*/
int ipfs_bitswap_network_send_message(const struct BitswapContext* context, struct Libp2pPeer* peer, const struct BitswapMessage* message) {
libp2p_logger_debug("bitswap_network", "Sending bitswap message to %s.\n", libp2p_peer_id_to_string(peer));
// get a connection to the peer
if (peer->connection_type != CONNECTION_TYPE_CONNECTED || peer->sessionContext == NULL) {
libp2p_peer_connect(context->ipfsNode->dialer, peer, context->ipfsNode->peerstore, context->ipfsNode->repo->config->datastore, 10);
if(peer->connection_type != CONNECTION_TYPE_CONNECTED)
return 0;
}
// protobuf the message
size_t buf_size = ipfs_bitswap_message_protobuf_encode_size(message);
uint8_t* buf = (uint8_t*) malloc(buf_size + 20);
if (buf == NULL)
return 0;
if (!ipfs_bitswap_message_protobuf_encode(message, &buf[20], buf_size, &buf_size)) {
free(buf);
return 0;
}
// tack on the protocol header
memcpy(buf, "/ipfs/bitswap/1.1.0\n", 20);
buf_size += 20;
// send it
struct StreamMessage outgoing;
outgoing.data = buf;
outgoing.data_size = buf_size;
int bytes_written = peer->sessionContext->default_stream->write(peer->sessionContext, &outgoing);
if (bytes_written <= 0) {
free(buf);
return 0;
}
free(buf);
return 1;
}
/***
* Remove a cid from the queue
* @param cids the vector of cids
* @param cid the cid to remove
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_bitswap_network_adjust_cid_queue(struct Libp2pVector* collection, struct Cid* cid, int cancel) {
if (collection == NULL || cid == NULL)
return 0;
for(int i = 0; i < collection->total; collection++) {
const struct CidEntry* current = (const struct CidEntry*)libp2p_utils_vector_get(collection, i);
if (ipfs_cid_compare(current->cid, cid) == 0) {
if (cancel)
libp2p_utils_vector_delete(collection, i);
return 1;
}
}
// not found. Add it if we're not cancelling
if (!cancel) {
struct CidEntry* cidEntry = ipfs_bitswap_peer_request_cid_entry_new();
cidEntry->cid = cid;
cidEntry->cancel = 0;
libp2p_utils_vector_add(collection, cidEntry);
}
return 0;
}
/***
* Handle a raw incoming bitswap message from the network
* @param node us
* @param sessionContext the connection context
* @param bytes the message
* @param bytes_size the size of the message
* @returns true(1) on success, false(0) otherwise.
*/
int ipfs_bitswap_network_handle_message(const struct IpfsNode* node, const struct SessionContext* sessionContext, const uint8_t* bytes, size_t bytes_length) {
struct BitswapContext* bitswapContext = (struct BitswapContext*)node->exchange->exchangeContext;
// strip off the protocol header
int start = -1;
for(int i = 0; i < bytes_length; i++) {
if (bytes[i] == '\n') {
start = i+1;
break;
}
}
if (start == -1)
return 0;
// un-protobuf the message
struct BitswapMessage* message = NULL;
if (!ipfs_bitswap_message_protobuf_decode(&bytes[start], bytes_length - start, &message))
return 0;
// process the message
// payload - what we want
if (message->payload != NULL) {
for(int i = 0; i < message->payload->total; i++) {
struct Block* blk = (struct Block*)libp2p_utils_vector_get(message->payload, i);
// we need a copy of the block so it survives the destruction of the message
node->exchange->HasBlock(node->exchange, ipfs_block_copy(blk));
}
}
// wantlist - what they want
if (message->wantlist != NULL && message->wantlist->entries != NULL && message->wantlist->entries->total > 0) {
// get the peer
if (sessionContext->remote_peer_id == NULL) {
ipfs_bitswap_message_free(message);
return 0;
}
struct Libp2pPeer* peer = libp2p_peerstore_get_or_add_peer_by_id(node->peerstore, (unsigned char*)sessionContext->remote_peer_id, strlen(sessionContext->remote_peer_id));
if (peer == NULL) {
libp2p_logger_error("bitswap_network", "Unable to find or add peer %s of length %d to peerstore.\n", sessionContext->remote_peer_id, strlen(sessionContext->remote_peer_id));
ipfs_bitswap_message_free(message);
return 0;
}
// find the queue (adds it if it is not there)
struct PeerRequest* peerRequest = ipfs_peer_request_queue_find_peer(bitswapContext->peerRequestQueue, peer);
for(int i = 0; i < message->wantlist->entries->total; i++) {
struct WantlistEntry* entry = (struct WantlistEntry*) libp2p_utils_vector_get(message->wantlist->entries, i);
// turn the "block" back into a cid
struct Cid* cid = NULL;
if (!ipfs_cid_protobuf_decode(entry->block, entry->block_size, &cid) || cid->hash_length == 0) {
libp2p_logger_error("bitswap_network", "Message had invalid CID\n");
ipfs_cid_free(cid);
ipfs_bitswap_message_free(message);
return 0;
}
ipfs_bitswap_network_adjust_cid_queue(peerRequest->cids_they_want, cid, entry->cancel);
}
}
ipfs_bitswap_message_free(message);
return 1;
}

View file

@ -1,447 +0,0 @@
/***
* A queue for requests from remote peers
* NOTE: This should handle multiple threads
*/
#include <stdlib.h>
#include "libp2p/conn/session.h"
#include "libp2p/utils/logger.h"
#include "ipfs/cid/cid.h"
#include "ipfs/exchange/bitswap/peer_request_queue.h"
#include "ipfs/exchange/bitswap/message.h"
#include "ipfs/exchange/bitswap/network.h"
/***
* Allocate memory for CidEntry
* @returns new CidEntry struct
*/
struct CidEntry* ipfs_bitswap_peer_request_cid_entry_new() {
struct CidEntry* entry = (struct CidEntry*) malloc(sizeof(struct CidEntry));
if (entry != NULL) {
entry->cid = NULL;
entry->cancel = 0;
entry->cancel_has_been_sent = 0;
entry->request_has_been_sent = 0;
}
return entry;
}
/**
* Allocate resources for a new PeerRequest
* @returns a new PeerRequest struct or NULL if there was a problem
*/
struct PeerRequest* ipfs_bitswap_peer_request_new() {
int retVal = 0;
struct PeerRequest* request = (struct PeerRequest*) malloc(sizeof(struct PeerRequest));
if (request != NULL) {
request->cids_they_want = libp2p_utils_vector_new(1);
if (request->cids_they_want == NULL)
goto exit;
request->cids_we_want = libp2p_utils_vector_new(1);
if (request->cids_we_want == NULL)
goto exit;
request->blocks_we_want_to_send = libp2p_utils_vector_new(1);
if (request->blocks_we_want_to_send == NULL)
goto exit;
request->peer = NULL;
}
retVal = 1;
exit:
if (retVal == 0 && request != NULL) {
if (request->blocks_we_want_to_send != NULL)
libp2p_utils_vector_free(request->blocks_we_want_to_send);
if (request->cids_they_want != NULL)
libp2p_utils_vector_free(request->cids_they_want);
if (request->cids_we_want != NULL)
libp2p_utils_vector_free(request->cids_we_want);
free(request);
request = NULL;
}
return request;
}
int ipfs_bitswap_cid_entry_free(struct CidEntry* entry) {
if (entry != NULL) {
if (entry->cid != NULL) {
ipfs_cid_free(entry->cid);
entry->cid = NULL;
}
free(entry);
}
return 1;
}
/**
* Free resources from a PeerRequest
* @param request the request to free
* @returns true(1)
*/
int ipfs_bitswap_peer_request_free(struct PeerRequest* request) {
if (request != NULL) {
for(int i = 0; i < request->cids_we_want->total; i++) {
struct CidEntry* entry = (struct CidEntry*)libp2p_utils_vector_get(request->cids_we_want, i);
ipfs_bitswap_cid_entry_free(entry);
}
libp2p_utils_vector_free(request->cids_we_want);
request->cids_we_want = NULL;
for(int i = 0; i < request->cids_they_want->total; i++) {
struct CidEntry* entry = (struct CidEntry*)libp2p_utils_vector_get(request->cids_they_want, i);
ipfs_bitswap_cid_entry_free(entry);
}
libp2p_utils_vector_free(request->cids_they_want);
request->cids_they_want = NULL;
for(int i = 0; i < request->blocks_we_want_to_send->total; i++) {
struct Block* block = (struct Block*)libp2p_utils_vector_get(request->blocks_we_want_to_send, i);
ipfs_block_free(block);
}
libp2p_utils_vector_free(request->blocks_we_want_to_send);
request->blocks_we_want_to_send = NULL;
free(request);
}
return 1;
}
/**
* Allocate resources for a new queue
*/
struct PeerRequestQueue* ipfs_bitswap_peer_request_queue_new() {
struct PeerRequestQueue* queue = malloc(sizeof(struct PeerRequestQueue));
if (queue != NULL) {
pthread_mutex_init(&queue->queue_mutex, NULL);
queue->first = NULL;
queue->last = NULL;
}
return queue;
}
/**
* Free all resources related to the queue
* @param queue the queue
* @returns true(1)
*/
int ipfs_bitswap_peer_request_queue_free(struct PeerRequestQueue* queue) {
pthread_mutex_lock(&queue->queue_mutex);
struct PeerRequestEntry* current = queue->last;
while (current != NULL) {
struct PeerRequestEntry* prior = current->prior;
ipfs_bitswap_peer_request_entry_free(current);
current = prior;
}
pthread_mutex_unlock(&queue->queue_mutex);
free(queue);
return 1;
}
/**
* Adds a peer request to the end of the queue
* @param queue the queue
* @param request the request
* @returns true(1) on success, otherwise false
*/
int ipfs_bitswap_peer_request_queue_add(struct PeerRequestQueue* queue, struct PeerRequest* request) {
if (request != NULL) {
struct PeerRequestEntry* entry = ipfs_bitswap_peer_request_entry_new();
entry->current = request;
pthread_mutex_lock(&queue->queue_mutex);
entry->prior = queue->last;
queue->last = entry;
if (queue->first == NULL) {
queue->first = entry;
}
pthread_mutex_unlock(&queue->queue_mutex);
return 1;
}
return 0;
}
/**
* Removes a peer request from the queue, no mather where it is
* @param queue the queue
* @param request the request
* @returns true(1) on success, otherwise false(0)
*/
int ipfs_bitswap_peer_request_queue_remove(struct PeerRequestQueue* queue, struct PeerRequest* request) {
if (request != NULL) {
struct PeerRequestEntry* entry = ipfs_bitswap_peer_request_queue_find_entry(queue, request->peer);
if (entry != NULL) {
pthread_mutex_lock(&queue->queue_mutex);
// remove the entry's link, and hook prior and next together
entry->prior->next = entry->next;
entry->prior = NULL;
entry->next = NULL;
ipfs_bitswap_peer_request_entry_free(entry);
pthread_mutex_unlock(&queue->queue_mutex);
return 1;
}
}
return 0;
}
/**
* Finds a PeerRequestEntry that contains the specified Peer
* @param queue the queue to look through
* @param peer what we're looking for
* @returns the PeerRequestEntry or NULL if not found
*/
struct PeerRequestEntry* ipfs_bitswap_peer_request_queue_find_entry(struct PeerRequestQueue* queue, struct Libp2pPeer* peer) {
if (peer != NULL) {
struct PeerRequestEntry* current = queue->first;
while (current != NULL) {
if (libp2p_peer_compare(current->current->peer, peer) == 0)
return current;
current = current->next;
}
}
return NULL;
}
/***
* Determine if any of the cids in the list are waiting to be filled
* @param cidEntries a Vector of CidEntry objects
* @returns true(1) if we have some waiting, false(0) otherwise
*/
int ipfs_bitswap_peer_request_cids_waiting(struct Libp2pVector* cidEntries) {
if (cidEntries == NULL)
return 0;
for(int i = 0; i < cidEntries->total; i++) {
const struct CidEntry* entry = (const struct CidEntry*)libp2p_utils_vector_get(cidEntries, i);
if (entry != NULL && !entry->cancel)
return 1;
}
return 0;
}
/***
* Determine if there is something to process in this request
* @param entry the entry to look at
* @returns true(1) if there is something to do
*/
int ipfs_bitswap_peer_request_something_to_do(struct PeerRequestEntry* entry) {
if (entry != NULL) {
struct PeerRequest* request = entry->current;
// do we have something in the queue?
if (request->blocks_we_want_to_send->total > 0)
return 1;
if (request->cids_we_want->total > 0)
return 1;
if (ipfs_bitswap_peer_request_cids_waiting(request->cids_they_want))
return 1;
// is there something waiting for us on the network?
if (request->peer->connection_type == CONNECTION_TYPE_CONNECTED) {
int retVal = request->peer->sessionContext->default_stream->peek(request->peer->sessionContext);
if (retVal < 0) {
libp2p_logger_debug("peer_request_queue", "Connection returned %d. Marking connection NOT CONNECTED.\n", retVal);
libp2p_peer_handle_connection_error(request->peer);
return 0;
}
if (retVal > 0) {
libp2p_logger_debug("peer_request_queue", "We have something to read. %d bytes.\n", retVal);
}
return retVal;
}
}
return 0;
}
/**
* Pull a PeerRequest off the queue
* @param queue the queue
* @returns the PeerRequest that should be handled next, or NULL if the queue is empty
*/
struct PeerRequest* ipfs_bitswap_peer_request_queue_pop(struct PeerRequestQueue* queue) {
struct PeerRequest* retVal = NULL;
if (queue != NULL) {
pthread_mutex_lock(&queue->queue_mutex);
struct PeerRequestEntry* entry = queue->first;
if (entry != NULL) {
if (ipfs_bitswap_peer_request_something_to_do(entry)) {
retVal = entry->current;
// move to the end of the queue
if (queue->first->next != NULL) {
queue->first = queue->first->next;
queue->last->next = entry;
queue->last = entry;
}
}
}
pthread_mutex_unlock(&queue->queue_mutex);
// disable temporarily
// JMJ Debugging
/*
if (entry != NULL)
ipfs_bitswap_peer_request_entry_free(entry);
*/
}
return retVal;
}
/***
* Allocate resources for a PeerRequestEntry struct
* @returns the allocated struct or NULL if there was a problem
*/
struct PeerRequestEntry* ipfs_bitswap_peer_request_entry_new() {
struct PeerRequestEntry* entry = (struct PeerRequestEntry*) malloc(sizeof(struct PeerRequestEntry));
if (entry != NULL) {
entry->current = NULL;
entry->next = NULL;
entry->prior = NULL;
}
return entry;
}
/**
* Frees resources allocated
* @param entry the PeerRequestEntry to free
* @returns true(1)
*/
int ipfs_bitswap_peer_request_entry_free(struct PeerRequestEntry* entry) {
entry->next = NULL;
entry->prior = NULL;
ipfs_bitswap_peer_request_free(entry->current);
entry->current = NULL;
free(entry);
return 1;
}
/***
* Add a block to the appropriate peer's queue
* @param queue the queue
* @param who the session context that identifies the peer
* @param block the block
* @returns true(1) on success, otherwise false(0)
*/
int ipfs_bitswap_peer_request_queue_fill(struct PeerRequestQueue* queue, struct Libp2pPeer* who, struct Block* block) {
// find the right entry
struct PeerRequest* entry = ipfs_peer_request_queue_find_peer(queue, who);
if (entry != NULL)
{
// add to the block array
libp2p_utils_vector_add(entry->blocks_we_want_to_send, block);
}
return 0;
}
/****
* Find blocks they want, and put them in the request
*/
int ipfs_bitswap_peer_request_get_blocks_they_want(const struct BitswapContext* context, struct PeerRequest* request) {
for(int i = 0; i < request->cids_they_want->total; i++) {
struct CidEntry* cidEntry = (struct CidEntry*)libp2p_utils_vector_get(request->cids_they_want, i);
if (cidEntry != NULL && !cidEntry->cancel) {
struct Block* block = NULL;
context->ipfsNode->blockstore->Get(context->ipfsNode->blockstore->blockstoreContext, cidEntry->cid, &block);
if (block != NULL) {
libp2p_utils_vector_add(request->blocks_we_want_to_send, block);
cidEntry->cancel = 1;
}
}
}
return 0;
}
/***
* Determine if we have anything we want (that we haven't sent already)
* @param cid_entries the list of CidEntries that are in our queue to be sent
* @returns true(1) if we have something to send, false(0) otherwise
*/
int ipfs_bitswap_peer_request_we_want_cids(struct Libp2pVector* cid_entries) {
if (cid_entries == NULL)
return 0;
if (cid_entries->total == 0)
return 0;
for(int i = 0; i < cid_entries->total; i++) {
const struct CidEntry* entry = (const struct CidEntry*) libp2p_utils_vector_get(cid_entries, i);
if (entry->cancel && !entry->cancel_has_been_sent)
return 1;
if (!entry->cancel && !entry->request_has_been_sent)
return 1;
}
return 0;
}
/****
* Handle a PeerRequest
* @param context the BitswapContext
* @param request the request to process
* @returns true(1) if something was done, otherwise false(0)
*/
int ipfs_bitswap_peer_request_process_entry(const struct BitswapContext* context, struct PeerRequest* request) {
// determine if we have enough information to continue
if (request == NULL)
return 0;
if (request->peer == NULL)
return 0;
if (!request->peer->is_local) {
if (request->peer->connection_type != CONNECTION_TYPE_CONNECTED)
if (request->peer->addr_head == NULL || request->peer->addr_head->item == NULL)
return 0;
}
// determine if we're connected
int connected = request->peer->is_local || request->peer->connection_type == CONNECTION_TYPE_CONNECTED;
int need_to_connect = ipfs_bitswap_peer_request_we_want_cids(request->cids_we_want) || ipfs_bitswap_peer_request_cids_waiting(request->cids_they_want) || request->blocks_we_want_to_send->total != 0;
// determine if we need to connect
if (need_to_connect) {
if (!connected) {
// connect
connected = libp2p_peer_connect(context->ipfsNode->dialer, request->peer, context->ipfsNode->peerstore, context->ipfsNode->repo->config->datastore, 0);
}
if (connected) {
// build a message
struct BitswapMessage* msg = ipfs_bitswap_message_new();
// see if we can fulfill any of their requests. If so, fill in msg->payload
ipfs_bitswap_peer_request_get_blocks_they_want(context, request);
ipfs_bitswap_message_add_blocks(msg, request->blocks_we_want_to_send, request->cids_they_want);
// add requests that we would like
ipfs_bitswap_message_add_wantlist_items(msg, request->cids_we_want);
// send message
if (ipfs_bitswap_network_send_message(context, request->peer, msg)) {
ipfs_bitswap_message_free(msg);
return 1;
}
ipfs_bitswap_message_free(msg);
}
}
return 0;
}
/***
* Find a PeerRequest related to a peer. If one is not found, it is created.
*
* @param peer_request_queue the queue to look through
* @param peer the peer to look for
* @returns a PeerRequestEntry or NULL on error
*/
struct PeerRequest* ipfs_peer_request_queue_find_peer(struct PeerRequestQueue* queue, struct Libp2pPeer* peer) {
struct PeerRequestEntry* entry = queue->first;
while (entry != NULL) {
if (libp2p_peer_compare(entry->current->peer, peer) == 0) {
return entry->current;
}
entry = entry->next;
}
// we didn't find one, so create one
entry = ipfs_bitswap_peer_request_entry_new();
entry->current = ipfs_bitswap_peer_request_new();
entry->current->peer = peer;
// attach it to the queue
if (queue->first == NULL) {
queue->first = entry;
queue->last = entry;
} else {
queue->last->next = entry;
entry->prior = queue->last;
queue->last = entry;
}
return entry->current;
}

View file

@ -1,66 +0,0 @@
#include <pthread.h>
#include "ipfs/exchange/bitswap/want_manager.h"
#include "ipfs/exchange/bitswap/wantlist_queue.h"
/***
* Add a Cid to the wantlist
* @param context the context
* @param cid the Cid
* @returns the added entry
*/
struct WantListQueueEntry* ipfs_bitswap_want_manager_add(const struct BitswapContext* context, const struct Cid* cid, const struct WantListSession* session) {
// add if not there, and increment reference count
return ipfs_bitswap_wantlist_queue_add(context->localWantlist, cid, session);
}
/***
* Checks to see if the requested block has been received
* @param context the context
* @param cid the Cid
* @returns true(1) if it has been received, false(0) otherwise
*/
int ipfs_bitswap_want_manager_received(const struct BitswapContext* context, const struct Cid* cid) {
// find the entry
struct WantListQueueEntry* entry = ipfs_bitswap_wantlist_queue_find(context->localWantlist, cid);
// check the status
if (entry != NULL && entry->block != NULL) {
return 1;
}
return 0;
}
/***
* retrieve a block from the WantManager. NOTE: a call to want_manager_received should be done first
* @param context the context
* @param cid the Cid to get
* @param block a pointer to the block that will be allocated
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_bitswap_want_manager_get_block(const struct BitswapContext* context, const struct Cid* cid, struct Block** block) {
struct WantListQueueEntry* entry = ipfs_bitswap_wantlist_queue_find(context->localWantlist, cid);
if (entry != NULL && entry->block != NULL) {
// return a copy of the block
*block = ipfs_block_copy(entry->block);
if ( (*block) != NULL) {
return 1;
}
}
return 0;
}
/***
* We no longer are requesting this block, so remove it from the queue
* NOTE: This is reference counted, as another process may have asked for it.
* @param context the context
* @param cid the Cid
* @returns true(1) on success, false(0) otherwise.
*/
int ipfs_bitswap_want_manager_remove(const struct BitswapContext* context, const struct Cid* cid) {
// decrement the reference count
// if it is zero, remove the entry (lock first)
struct WantListSession session;
session.type = WANTLIST_SESSION_TYPE_LOCAL;
session.context = (void*) context->ipfsNode;
return ipfs_bitswap_wantlist_queue_remove(context->localWantlist, cid, &session);
}

View file

@ -1,335 +0,0 @@
#include <stdlib.h>
#include "libp2p/conn/session.h"
#include "libp2p/utils/vector.h"
#include "ipfs/exchange/bitswap/wantlist_queue.h"
#include "ipfs/exchange/bitswap/peer_request_queue.h"
/**
* Implementation of the WantlistQueue
*/
/**
* remove this session from the lists of sessions that are looking for this WantListQueueEntry
* @param entry the entry
* @param session who was looking for it
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_bitswap_wantlist_queue_entry_decrement(struct WantListQueueEntry* entry, const struct WantListSession* session) {
for(size_t i = 0; i < entry->sessionsRequesting->total; i++) {
const struct WantListSession* current = (const struct WantListSession*)libp2p_utils_vector_get(entry->sessionsRequesting, i);
if (ipfs_bitswap_wantlist_session_compare(session, current) == 0) {
libp2p_utils_vector_delete(entry->sessionsRequesting, i);
return 1;
}
}
return 0;
}
/***
* Initialize a new Wantlist (there should only be 1 per instance)
* @returns a new WantList
*/
struct WantListQueue* ipfs_bitswap_wantlist_queue_new() {
struct WantListQueue* wantlist = (struct WantListQueue*) malloc(sizeof(struct WantListQueue));
if (wantlist != NULL) {
pthread_mutex_init(&wantlist->wantlist_mutex, NULL);
wantlist->queue = NULL;
}
return wantlist;
}
/***
* Deallocate resources of a WantList
* @param wantlist the WantList
* @returns true(1)
*/
int ipfs_bitswap_wantlist_queue_free(struct WantListQueue* wantlist) {
if (wantlist != NULL) {
if (wantlist->queue != NULL) {
for(int i = 0; i < wantlist->queue->total; i++) {
struct WantListQueueEntry* entry = (struct WantListQueueEntry*)libp2p_utils_vector_get(wantlist->queue, i);
ipfs_bitswap_wantlist_queue_entry_free(entry);
}
libp2p_utils_vector_free(wantlist->queue);
wantlist->queue = NULL;
}
free(wantlist);
}
return 1;
}
/***
* Add a Cid to the WantList
* @param wantlist the WantList to add to
* @param cid the Cid to add
* @returns the correct WantListEntry or NULL if error
*/
struct WantListQueueEntry* ipfs_bitswap_wantlist_queue_add(struct WantListQueue* wantlist, const struct Cid* cid, const struct WantListSession* session) {
struct WantListQueueEntry* entry = NULL;
if (wantlist != NULL) {
pthread_mutex_lock(&wantlist->wantlist_mutex);
if (wantlist->queue == NULL) {
wantlist->queue = libp2p_utils_vector_new(1);
}
entry = ipfs_bitswap_wantlist_queue_find(wantlist, cid);
if (entry == NULL) {
// create a new one
entry = ipfs_bitswap_wantlist_queue_entry_new();
entry->cid = ipfs_cid_copy(cid);
entry->priority = 1;
libp2p_utils_vector_add(entry->sessionsRequesting, session);
libp2p_utils_vector_add(wantlist->queue, entry);
}
libp2p_utils_vector_add(entry->sessionsRequesting, session);
pthread_mutex_unlock(&wantlist->wantlist_mutex);
}
return entry;
}
/***
* Remove (decrement the counter) a Cid from the WantList
* @param wantlist the WantList
* @param cid the Cid
* @returns true(1) on success, otherwise false(0)
*/
int ipfs_bitswap_wantlist_queue_remove(struct WantListQueue* wantlist, const struct Cid* cid, const struct WantListSession* session) {
//TODO: remove if counter is <= 0
if (wantlist != NULL) {
struct WantListQueueEntry* entry = ipfs_bitswap_wantlist_queue_find(wantlist, cid);
if (entry != NULL) {
ipfs_bitswap_wantlist_queue_entry_decrement(entry, session);
return 1;
}
}
return 0;
}
/***
* Find a Cid in the WantList
* @param wantlist the list
* @param cid the Cid
* @returns the WantListQueueEntry
*/
struct WantListQueueEntry* ipfs_bitswap_wantlist_queue_find(struct WantListQueue* wantlist, const struct Cid* cid) {
for (size_t i = 0; i < wantlist->queue->total; i++) {
struct WantListQueueEntry* entry = (struct WantListQueueEntry*) libp2p_utils_vector_get(wantlist->queue, i);
if (entry == NULL) {
//TODO: something went wrong. This should be logged.
return NULL;
}
if (ipfs_cid_compare(cid, entry->cid) == 0) {
return entry;
}
}
return NULL;
}
/***
* Pops the top one off the queue
*
* @param wantlist the list
* @returns the WantListQueueEntry
*/
struct WantListQueueEntry* ipfs_bitswap_wantlist_queue_pop(struct WantListQueue* wantlist) {
struct WantListQueueEntry* entry = NULL;
if (wantlist == NULL || wantlist->queue == NULL || wantlist->queue->total == 0)
return entry;
//TODO: This should be a linked list, not an array
pthread_mutex_lock(&wantlist->wantlist_mutex);
for(int i = 0; i < wantlist->queue->total; i++) {
struct WantListQueueEntry* current = (struct WantListQueueEntry*)libp2p_utils_vector_get(wantlist->queue, i);
if (current->block == NULL && !current->asked_network) {
entry = current;
break;
}
}
//libp2p_utils_vector_delete(wantlist->queue, 0);
pthread_mutex_unlock(&wantlist->wantlist_mutex);
return entry;
}
/***
* Initialize a WantListQueueEntry
* @returns a new WantListQueueEntry
*/
struct WantListQueueEntry* ipfs_bitswap_wantlist_queue_entry_new() {
struct WantListQueueEntry* entry = (struct WantListQueueEntry*) malloc(sizeof(struct WantListQueueEntry));
if (entry != NULL) {
entry->sessionsRequesting = libp2p_utils_vector_new(1);
if (entry->sessionsRequesting == NULL) {
free(entry);
return NULL;
}
entry->block = NULL;
entry->cid = NULL;
entry->priority = 0;
entry->attempts = 0;
entry->asked_network = 0;
}
return entry;
}
/***
* Free a WantListQueueENtry struct
* @param entry the struct
* @returns true(1)
*/
int ipfs_bitswap_wantlist_queue_entry_free(struct WantListQueueEntry* entry) {
if (entry != NULL) {
if (entry->block != NULL) {
ipfs_block_free(entry->block);
entry->block = NULL;
}
if (entry->cid != NULL) {
ipfs_cid_free(entry->cid);
entry->cid = NULL;
}
if (entry->sessionsRequesting != NULL) {
libp2p_utils_vector_free(entry->sessionsRequesting);
entry->sessionsRequesting = NULL;
}
free(entry);
}
return 1;
}
int ipfs_bitswap_wantlist_session_compare(const struct WantListSession* a, const struct WantListSession* b) {
if (a == NULL && b == NULL)
return 0;
if (a == NULL && b != NULL)
return -1;
if (a != NULL && b == NULL)
return 1;
if (a->type != b->type)
return b->type - a->type;
if (a->type == WANTLIST_SESSION_TYPE_LOCAL) {
// it's local, there should be only 1
return 0;
} else {
struct Libp2pPeer* contextA = (struct Libp2pPeer*)a->context;
struct Libp2pPeer* contextB = (struct Libp2pPeer*)b->context;
return libp2p_peer_compare(contextA, contextB);
}
}
/**
* Create a new WantListSession
* @returns the newly allocated WantListSession
*/
struct WantListSession* ipfs_bitswap_wantlist_session_new() {
struct WantListSession* ret = (struct WantListSession*) malloc(sizeof(struct WantListSession));
if (ret != NULL) {
ret->context = NULL;
ret->type = WANTLIST_SESSION_TYPE_LOCAL;
}
return ret;
}
/**
* determine if any of the sessions are referring to the local node
* @param sessions a vector of WantlistSession
* @returns true(1) if any of the sessions are local, false otherwise
*/
int ipfs_bitswap_wantlist_local_request(struct Libp2pVector* sessions)
{
for(int i = 0; i < sessions->total; i++) {
struct WantListSession* curr = (struct WantListSession*) libp2p_utils_vector_get(sessions, i);
if (curr != NULL && curr->type == WANTLIST_SESSION_TYPE_LOCAL)
return 1;
}
return 0;
}
/***
* Attempt to retrieve a block from the local blockstore
*
* @param context the BitswapContext
* @param cid the id to look for
* @param block where to put the results
* @returns true(1) if found, false(0) otherwise
*/
int ipfs_bitswap_wantlist_get_block_locally(struct BitswapContext* context, struct Cid* cid, struct Block** block) {
return context->ipfsNode->blockstore->Get(context->ipfsNode->blockstore->blockstoreContext, cid, block);
}
/***
* Retrieve a block. The only information we have is the cid
*
* This will ask the network for who has the file, using the router.
* It will then ask the specific nodes for the file. This method
* does not queue anything. It actually does the work. The remotes
* will queue the file, but we'll return before they respond.
*
* @param context the BitswapContext
* @param cid the id of the file
* @returns true(1) if we found some providers to ask, false(0) otherwise
*/
int ipfs_bitswap_wantlist_get_block_remote(struct BitswapContext* context, struct Cid* cid) {
// find out who may have the file
struct Libp2pVector* providers = NULL;
if (context->ipfsNode->routing->FindProviders(context->ipfsNode->routing, cid->hash, cid->hash_length, &providers)) {
for(int i = 0; i < providers->total; i++) {
struct Libp2pPeer* current = (struct Libp2pPeer*) libp2p_utils_vector_get(providers, i);
// add this to their queue
struct PeerRequest* queueEntry = ipfs_peer_request_queue_find_peer(context->peerRequestQueue, current);
struct CidEntry* entry = ipfs_bitswap_peer_request_cid_entry_new();
entry->cid = ipfs_cid_copy(cid);
libp2p_utils_vector_add(queueEntry->cids_we_want, entry);
// process this queue via bitswap protocol
ipfs_bitswap_peer_request_process_entry(context, queueEntry);
//libp2p_peer_free(current);
}
libp2p_utils_vector_free(providers);
return 1;
}
return 0;
}
/**
* Called by the Bitswap engine, this processes an item on the WantListQueue. This is called when
* we want a file locally from a remote source. Send a message immediately, adding in stuff that
* perhaps the remote source wanted.
*
* @param context the context
* @param entry the WantListQueueEntry
* @returns true(1) on success, false(0) if not.
*/
int ipfs_bitswap_wantlist_process_entry(struct BitswapContext* context, struct WantListQueueEntry* entry) {
int local_request = ipfs_bitswap_wantlist_local_request(entry->sessionsRequesting);
int have_local = ipfs_bitswap_wantlist_get_block_locally(context, entry->cid, &entry->block);
// should we go get it?
if (!local_request && !have_local) {
return 0;
}
if (local_request && !have_local) {
if (!ipfs_bitswap_wantlist_get_block_remote(context, entry->cid)) {
// if we were unsuccessful in retrieving it, put it back in the queue?
// I don't think so. But I'm keeping this counter here until we have
// a final decision. Maybe lower the priority?
entry->attempts++;
return 0;
} else {
entry->asked_network = 1;
}
}
if (entry->block != NULL) {
// okay we have the block.
// fulfill the requests
for(size_t i = 0; i < entry->sessionsRequesting->total; i++) {
// TODO: Review this code.
struct WantListSession* session = (struct WantListSession*) libp2p_utils_vector_get(entry->sessionsRequesting, i);
if (session->type == WANTLIST_SESSION_TYPE_LOCAL) {
//context->ipfsNode->exchange->HasBlock(context->ipfsNode->exchange, entry->block);
} else {
struct Libp2pPeer* peer = (struct Libp2pPeer*) session->context;
ipfs_bitswap_peer_request_queue_fill(context->peerRequestQueue, peer, entry->block);
}
}
}
return 0;
}

View file

@ -1,6 +1,6 @@
CC = gcc
CFLAGS = -O0 -I../include -I../c-libp2p/include -Wall -std=gnu99
CFLAGS = -O0 -I../include -I../../c-libp2p/include
ifdef DEBUG
CFLAGS += -g3

View file

@ -9,7 +9,7 @@
#include <string.h>
#include <sys/stat.h>
#include "libp2p/os/utils.h"
#include "ipfs/os/utils.h"
#define FLATFS_MAX_PREFIX_LENGTH 16
@ -50,13 +50,8 @@ int ipfs_flatfs_create_directory(const char* full_directory) {
return 0;
}
// it is not there, create it
#ifdef __MINGW32__
if (mkdir(full_directory) == -1)
return 0;
#else
if (mkdir(full_directory, S_IRWXU) == -1)
return 0;
#endif
return 1;
}

View file

@ -1,18 +0,0 @@
CC = gcc
CFLAGS = -O0 -I../include -I../c-libp2p/include -I../c-libp2p/c-multihash/include -I../c-libp2p/c-multiaddr/include -I../c-libp2p/c-protobuf -Wall -std=gnu99
ifdef DEBUG
CFLAGS += -g3
endif
LFLAGS =
DEPS =
OBJS = importer.o exporter.o resolver.o
%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
all: $(OBJS)
clean:
rm -f *.o

View file

@ -1,340 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include "ipfs/cid/cid.h"
#include "ipfs/core/http_request.h"
#include "ipfs/importer/exporter.h"
#include "ipfs/merkledag/merkledag.h"
#include "ipfs/merkledag/node.h"
#include "ipfs/repo/fsrepo/fs_repo.h"
#include "ipfs/repo/init.h"
#include "ipfs/core/ipfs_node.h"
#include "libp2p/utils/logger.h"
#include "ipfs/namesys/name.h"
#include "ipfs/repo/fsrepo/jsmn.h"
/**
* pull objects from ipfs
*/
/***
* Helper method to retrieve a protobuf'd Node from the router
* @param local_node the context
* @param hash the hash to retrieve
* @param hash_size the length of the hash
* @param result a place to store the Node
* @returns true(1) on success, otherwise false(0)
*/
int ipfs_exporter_get_node(struct IpfsNode* local_node, const unsigned char* hash, const size_t hash_size,
struct HashtableNode** result) {
unsigned char *buffer = NULL;
size_t buffer_size = 0;
int retVal = 0;
struct KademliaMessage* msg = NULL;
if (!local_node->routing->GetValue(local_node->routing, hash, hash_size, (void**)&buffer, &buffer_size)) {
libp2p_logger_debug("exporter", "get_node got no value. Returning false.\n");
goto exit;
}
// unprotobuf
if (!ipfs_hashtable_node_protobuf_decode(buffer, buffer_size, result)) {
libp2p_logger_error("exporter", "Conversion to HashtableNode not successful\n");
goto exit;
}
// copy in the hash
(*result)->hash_size = hash_size;
(*result)->hash = malloc(hash_size);
if ( (*result)->hash == NULL) {
// memory issue
libp2p_logger_error("exporter", "get_node: Unable to allocate memory.\n");
goto exit;
}
memcpy((*result)->hash, hash, hash_size);
retVal = 1;
exit:
if (buffer != NULL)
free(buffer);
if (msg != NULL)
libp2p_message_free(msg);
return retVal;
}
/***
* Get a file by its hash, and write the data to a filestream
* @param hash the base58 multihash of the cid
* @param file_descriptor where to write
* @param local_node the context
*/
int ipfs_exporter_to_filestream(const unsigned char* hash, FILE* file_descriptor, struct IpfsNode* local_node) {
// convert hash to cid
struct Cid* cid = NULL;
if ( ipfs_cid_decode_hash_from_base58(hash, strlen((char*)hash), &cid) == 0) {
return 0;
}
// find block
struct HashtableNode* read_node = NULL;
if (!ipfs_exporter_get_node(local_node, cid->hash, cid->hash_length, &read_node)) {
ipfs_cid_free(cid);
return 0;
}
// no longer need the cid
ipfs_cid_free(cid);
if (read_node->head_link == NULL) {
// convert the node's data into a UnixFS data block
struct UnixFS* unix_fs;
ipfs_unixfs_protobuf_decode(read_node->data, read_node->data_size, &unix_fs);
size_t bytes_written = fwrite(unix_fs->bytes, 1, unix_fs->bytes_size, file_descriptor);
if (bytes_written != unix_fs->bytes_size) {
ipfs_hashtable_node_free(read_node);
ipfs_unixfs_free(unix_fs);
return 0;
}
ipfs_unixfs_free(unix_fs);
} else {
struct NodeLink* link = read_node->head_link;
struct HashtableNode* link_node = NULL;
while (link != NULL) {
if ( !ipfs_exporter_get_node(local_node, link->hash, link->hash_size, &link_node)) {
ipfs_hashtable_node_free(read_node);
return 0;
}
struct UnixFS* unix_fs;
ipfs_unixfs_protobuf_decode(link_node->data, link_node->data_size, &unix_fs);
size_t bytes_written = fwrite(unix_fs->bytes, 1, unix_fs->bytes_size, file_descriptor);
if (bytes_written != unix_fs->bytes_size) {
ipfs_hashtable_node_free(link_node);
ipfs_hashtable_node_free(read_node);
ipfs_unixfs_free(unix_fs);
return 0;
}
ipfs_hashtable_node_free(link_node);
ipfs_unixfs_free(unix_fs);
link = link->next;
}
}
if (read_node != NULL)
ipfs_hashtable_node_free(read_node);
return 1;
}
/**
* get a file by its hash, and write the data to a file
* @param hash the base58 multihash of the cid
* @param file_name the file name to write to
* @returns true(1) on success
*/
int ipfs_exporter_to_file(const unsigned char* hash, const char* file_name, struct IpfsNode *local_node) {
// process blocks
FILE* file = fopen(file_name, "wb");
if (file == NULL) {
return 0;
}
int retVal = ipfs_exporter_to_filestream(hash, file, local_node);
fclose(file);
return retVal;
}
/**
* get a file by its hash, and write the data to a file
* @param hash the base58 multihash of the cid
* @param file_name the file name to write to
* @returns true(1) on success
*/
int ipfs_exporter_to_console(const unsigned char* hash, struct IpfsNode *local_node) {
// convert hash to cid
struct Cid* cid = NULL;
if ( ipfs_cid_decode_hash_from_base58(hash, strlen((char*)hash), &cid) == 0) {
return 0;
}
// find block
struct HashtableNode* read_node = NULL;
if (!ipfs_exporter_get_node(local_node, cid->hash, cid->hash_length, &read_node)) {
ipfs_cid_free(cid);
return 0;
}
// no longer need the cid
ipfs_cid_free(cid);
// process blocks
struct NodeLink* link = read_node->head_link;
printf("{Links:[");
while (link != NULL) {
unsigned char b58[100];
ipfs_cid_hash_to_base58(link->hash, link->hash_size, b58, 100);
printf("{\"Name\":\"%s\",\"Hash\":\"%s\",\"Size\":%lu}", (link->name != NULL ? link->name : ""), (char*)b58, link->t_size);
link = link->next;
}
printf("],\"Data\":\"");
for(size_t i = 0LU; i < read_node->data_size; i++) {
printf("%02x", read_node->data[i]);
}
printf("\"}\n");
if (read_node != NULL)
ipfs_hashtable_node_free(read_node);
return 1;
}
/***
* Called from the command line with ipfs object get [hash].
* Retrieves the object pointed to by hash, and displays
* its block data (links and data elements)
* @param argc number of arguments
* @param argv arguments
* @returns true(1) on success
*/
int ipfs_exporter_object_get(int argc, char** argv) {
char* repo_path = NULL;
if (!ipfs_repo_get_directory(argc, argv, &repo_path)) {
fprintf(stderr, "Unable to open repository: %s\n", repo_path);
return 0;
}
struct IpfsNode* local_node = NULL;
if (!ipfs_node_online_new(repo_path, &local_node))
return 0;
// find hash
int retVal = ipfs_exporter_to_console((unsigned char*)argv[3], local_node);
ipfs_node_free(local_node);
return retVal;
}
/**
* rebuild a file based on this HashtableNode, traversing links
* @param node the HashtableNode to start with
* @param local_node the context
* @param file the filestream to fill
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_exporter_cat_node(struct HashtableNode* node, struct IpfsNode* local_node, FILE *file) {
// process this node, then move on to the links
// build the unixfs
struct UnixFS* unix_fs;
if (!ipfs_unixfs_protobuf_decode(node->data, node->data_size, &unix_fs)) {
return 0;
}
for(size_t i = 0LU; i < unix_fs->bytes_size; i++) {
fprintf(file, "%c", unix_fs->bytes[i]);
}
ipfs_unixfs_free(unix_fs);
// process links
struct NodeLink* current = node->head_link;
while (current != NULL) {
// find the node
struct HashtableNode* child_node = NULL;
if (!ipfs_exporter_get_node(local_node, current->hash, current->hash_size, &child_node)) {
return 0;
}
ipfs_exporter_cat_node(child_node, local_node, file);
ipfs_hashtable_node_free(child_node);
current = current->next;
}
return 1;
}
int ipfs_exporter_object_cat_to_file(struct IpfsNode *local_node, unsigned char* hash, int hash_size, FILE* file) {
struct HashtableNode* read_node = NULL;
// find block
if (!ipfs_exporter_get_node(local_node, hash, hash_size, &read_node)) {
return 0;
}
int retVal = ipfs_exporter_cat_node(read_node, local_node, file);
ipfs_hashtable_node_free(read_node);
return retVal;
}
/***
* Called from the command line with ipfs cat [hash]. Retrieves the object
* pointed to by hash, and displays its raw block data to the console
* @param argc number of arguments
* @param argv arguments
* @returns true(1) on success
*/
int ipfs_exporter_object_cat(struct CliArguments* args, FILE* output_file) {
struct IpfsNode *local_node = NULL;
char* repo_dir = NULL;
if (!ipfs_repo_get_directory(args->argc, args->argv, &repo_dir)) {
libp2p_logger_error("exporter", "Unable to open repo: %s\n", repo_dir);
return 0;
}
if (!ipfs_node_offline_new(repo_dir, &local_node)) {
libp2p_logger_error("exporter", "Unable to create new offline node based on config at %s.\n", repo_dir);
return 0;
}
if (local_node->mode == MODE_API_AVAILABLE) {
const char ipns_prefix[] = "/ipns/";
const char ipfs_prefix[] = "/ipfs/";
char* hash = args->argv[args->verb_index + 1];
libp2p_logger_debug("exporter", "We're attempting to use the API for this object get of %s.\n", hash);
if (memcmp(hash, ipfs_prefix, sizeof(ipfs_prefix)-1) == 0) {
// skip ipfs_prefix;
hash += sizeof(ipfs_prefix)-1;
} else if (memcmp(hash, ipns_prefix, sizeof(ipns_prefix)-1) == 0) {
char *response = NULL;
size_t response_size;
if (ipfs_name_resolve(local_node, hash, &response, &response_size) && response && response_size > 0) {
hash = jsmn_simple_parser(response, response_size, "Path");
if (!hash) {
return 0;
}
}
}
struct HttpRequest* request = ipfs_core_http_request_new();
char* response = NULL;
request->command = "object";
request->sub_command = "get";
request->arguments = libp2p_utils_vector_new(1);
libp2p_utils_vector_add(request->arguments, hash);
size_t response_size = 0;
int retVal = ipfs_core_http_request_post(local_node, request, &response, &response_size, "", 0);
if (response != NULL && response_size > 0) {
fwrite(response, 1, response_size, output_file);
free(response);
} else {
retVal = 0;
}
ipfs_core_http_request_free(request);
return retVal;
} else {
libp2p_logger_debug("exporter", "API not available, using direct access.\n");
struct Cid* cid = NULL;
if ( ipfs_cid_decode_hash_from_base58((unsigned char*)args->argv[args->verb_index+1], strlen(args->argv[args->verb_index+1]), &cid) == 0) {
libp2p_logger_error("exporter", "Unable to decode hash from base58 [%s]\n", args->argv[args->verb_index+1]);
return 0;
}
int retVal = ipfs_exporter_object_cat_to_file(local_node, cid->hash, cid->hash_length, output_file);
ipfs_cid_free(cid);
return retVal;
}
return 0;
}

View file

@ -1,473 +0,0 @@
// these two for strdup
#define _GNU_SOURCE
#define __USE_GNU
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "ipfs/importer/importer.h"
#include "ipfs/merkledag/merkledag.h"
#include "libp2p/os/utils.h"
#include "ipfs/cmd/cli.h"
#include "ipfs/core/ipfs_node.h"
#include "ipfs/core/http_request.h"
#include "ipfs/repo/fsrepo/fs_repo.h"
#include "ipfs/repo/init.h"
#include "ipfs/unixfs/unixfs.h"
#define MAX_DATA_SIZE 262144 // 1024 * 256;
/***
* Imports OS files into the datastore
*/
/***
* adds a blocksize to the UnixFS structure stored in the data
* element of a Node
* @param node the node to work with
* @param blocksize the blocksize to add
* @returns true(1) on success
*/
int ipfs_importer_add_filesize_to_data_section(struct HashtableNode* node, size_t bytes_read) {
// now add to the data section
struct UnixFS* data_section = NULL;
if (node->data == NULL) {
// nothing in data section yet, create new UnixFS
ipfs_unixfs_new(&data_section);
data_section->data_type = UNIXFS_FILE;
} else {
ipfs_unixfs_protobuf_decode(node->data, node->data_size, &data_section);
}
struct UnixFSBlockSizeNode bs;
bs.block_size = bytes_read;
ipfs_unixfs_add_blocksize(&bs, data_section);
data_section->file_size += bytes_read;
// put the new data back in the data section
size_t protobuf_size = ipfs_unixfs_protobuf_encode_size(data_section); //delay bytes_size entry
unsigned char protobuf[protobuf_size];
ipfs_unixfs_protobuf_encode(data_section, protobuf, protobuf_size, &protobuf_size);
ipfs_unixfs_free(data_section);
ipfs_hashtable_node_set_data(node, protobuf, protobuf_size);
return 1;
}
/**
* read the next chunk of bytes, create a node, and add a link to the node in the passed-in node
* @param file the file handle
* @param node the node to add to
* @returns number of bytes read
*/
size_t ipfs_import_chunk(FILE* file, struct HashtableNode* parent_node, struct FSRepo* fs_repo, size_t* total_size, size_t* bytes_written) {
unsigned char buffer[MAX_DATA_SIZE];
size_t bytes_read = fread(buffer, 1, MAX_DATA_SIZE, file);
// structs used by this method
struct UnixFS* new_unixfs = NULL;
struct HashtableNode* new_node = NULL;
struct NodeLink* new_link = NULL;
// put the file bits into a new UnixFS file
if (ipfs_unixfs_new(&new_unixfs) == 0)
return 0;
new_unixfs->data_type = UNIXFS_FILE;
new_unixfs->file_size = bytes_read;
if (ipfs_unixfs_add_data(&buffer[0], bytes_read, new_unixfs) == 0) {
ipfs_unixfs_free(new_unixfs);
return 0;
}
// protobuf the UnixFS
size_t protobuf_size = ipfs_unixfs_protobuf_encode_size(new_unixfs);
if (protobuf_size == 0) {
ipfs_unixfs_free(new_unixfs);
return 0;
}
unsigned char protobuf[protobuf_size];
*bytes_written = 0;
if (ipfs_unixfs_protobuf_encode(new_unixfs, protobuf, protobuf_size, bytes_written) == 0) {
ipfs_unixfs_free(new_unixfs);
return 0;
}
// we're done with the UnixFS object
ipfs_unixfs_free(new_unixfs);
size_t size_of_node = 0;
// if there is more to read, create a new node.
if (bytes_read == MAX_DATA_SIZE) {
// create a new node
if (ipfs_hashtable_node_new_from_data(protobuf, *bytes_written, &new_node) == 0) {
return 0;
}
// persist
size_t size_of_node = 0;
if (ipfs_merkledag_add(new_node, fs_repo, &size_of_node) == 0) {
ipfs_hashtable_node_free(new_node);
return 0;
}
// put link in parent node
if (ipfs_node_link_create(NULL, new_node->hash, new_node->hash_size, &new_link) == 0) {
ipfs_hashtable_node_free(new_node);
return 0;
}
new_link->t_size = size_of_node;
*total_size += new_link->t_size;
// NOTE: disposal of this link object happens when the parent is disposed
if (ipfs_hashtable_node_add_link(parent_node, new_link) == 0) {
ipfs_hashtable_node_free(new_node);
return 0;
}
ipfs_importer_add_filesize_to_data_section(parent_node, bytes_read);
ipfs_hashtable_node_free(new_node);
*bytes_written = size_of_node;
size_of_node = 0;
} else {
// if there are no existing links, put what we pulled from the file into parent_node
// otherwise, add it as a link
if (parent_node->head_link == NULL) {
ipfs_hashtable_node_set_data(parent_node, protobuf, *bytes_written);
} else {
// there are existing links. put the data in a new node, save it, then put the link in parent_node
// create a new node
if (ipfs_hashtable_node_new_from_data(protobuf, *bytes_written, &new_node) == 0) {
return 0;
}
// persist
if (ipfs_merkledag_add(new_node, fs_repo, &size_of_node) == 0) {
ipfs_hashtable_node_free(new_node);
return 0;
}
// put link in parent node
if (ipfs_node_link_create(NULL, new_node->hash, new_node->hash_size, &new_link) == 0) {
ipfs_hashtable_node_free(new_node);
return 0;
}
new_link->t_size = size_of_node;
*total_size += new_link->t_size;
// NOTE: disposal of this link object happens when the parent is disposed
if (ipfs_hashtable_node_add_link(parent_node, new_link) == 0) {
ipfs_hashtable_node_free(new_node);
return 0;
}
ipfs_importer_add_filesize_to_data_section(parent_node, bytes_read);
ipfs_hashtable_node_free(new_node);
}
// persist the main node
ipfs_merkledag_add(parent_node, fs_repo, bytes_written);
*bytes_written += size_of_node;
} // add to parent vs add as link
return bytes_read;
}
/**
* Prints to the console the results of a node import
* @param node the node imported
* @param file_name the name of the file
* @returns true(1) if successful, false(0) if couldn't generate the MultiHash to be displayed
*/
int ipfs_import_print_node_results(const struct HashtableNode* node, const char* file_name) {
// give some results to the user
//TODO: if directory_entry is itself a directory, traverse and report files
int buffer_len = 100;
unsigned char buffer[buffer_len];
if (ipfs_cid_hash_to_base58(node->hash, node->hash_size, buffer, buffer_len) == 0) {
printf("Unable to generate hash for file %s.\n", file_name);
return 0;
}
printf("added %s %s\n", buffer, file_name);
return 1;
}
/**
* Creates a node based on an incoming file or directory
* NOTE: this can be called recursively for directories
* NOTE: When this function completes, parent_node will be either:
* 1) the complete file, in the case of a small file (<256k-ish)
* 2) a node with links to the various pieces of a large file
* 3) a node with links to files and directories if 'fileName' is a directory
* @param root_dir the directory for where to look for the file
* @param file_name the file (or directory) to import
* @param parent_node the root node (has links to others in case this is a large file and is split)
* @param fs_repo the ipfs repository
* @param bytes_written number of bytes written to disk
* @param recursive true if we should navigate directories
* @returns true(1) on success
*/
int ipfs_import_file(const char* root_dir, const char* fileName, struct HashtableNode** parent_node, struct IpfsNode* local_node, size_t* bytes_written, int recursive) {
/**
* NOTE: When this function completes, parent_node will be either:
* 1) the complete file, in the case of a small file (<256k-ish)
* 2) a node with links to the various pieces of a large file
* 3) a node with links to files and directories if 'fileName' is a directory
*/
int retVal = 1;
int bytes_read = MAX_DATA_SIZE;
size_t total_size = 0;
if (os_utils_is_directory(fileName)) {
// calculate the new root_dir
char* new_root_dir = (char*)root_dir;
char* path = NULL;
char* file = NULL;
os_utils_split_filename(fileName, &path, &file);
if (root_dir == NULL) {
new_root_dir = file;
} else {
free(path);
path = malloc(strlen(root_dir) + strlen(file) + 2);
if (path == NULL) {
// memory issue
if (file != NULL)
free(file);
return 0;
}
os_utils_filepath_join(root_dir, file, path, strlen(root_dir) + strlen(file) + 2);
new_root_dir = path;
}
// initialize parent_node as a directory
if (ipfs_hashtable_node_create_directory(parent_node) == 0) {
if (path != NULL)
free(path);
if (file != NULL)
free(file);
return 0;
}
// get list of files
struct FileList* first = os_utils_list_directory(fileName);
struct FileList* next = first;
if (recursive) {
while (next != NULL) {
// process each file. NOTE: could be an embedded directory
*bytes_written = 0;
struct HashtableNode* file_node;
// put the filename together from fileName, which is the directory, and next->file_name
// which is a file (or a directory) within the directory we just found.
size_t filename_len = strlen(fileName) + strlen(next->file_name) + 2;
char full_file_name[filename_len];
os_utils_filepath_join(fileName, next->file_name, full_file_name, filename_len);
// adjust root directory
if (ipfs_import_file(new_root_dir, full_file_name, &file_node, local_node, bytes_written, recursive) == 0) {
ipfs_hashtable_node_free(*parent_node);
os_utils_free_file_list(first);
if (file != NULL)
free(file);
if (path != NULL)
free (path);
return 0;
}
// TODO: probably need to display what was imported
int len = strlen(next->file_name) + strlen(new_root_dir) + 2;
char full_path[len];
os_utils_filepath_join(new_root_dir, next->file_name, full_path, len);
ipfs_import_print_node_results(file_node, full_path);
// TODO: Determine what needs to be done if this file_node is a file, a split file, or a directory
// Create link from file_node
struct NodeLink* file_node_link;
ipfs_node_link_create(next->file_name, file_node->hash, file_node->hash_size, &file_node_link);
file_node_link->t_size = *bytes_written;
// add file_node as link to parent_node
ipfs_hashtable_node_add_link(*parent_node, file_node_link);
// clean up file_node
ipfs_hashtable_node_free(file_node);
// move to next file in list
next = next->next;
} // while going through files
}
// save the parent_node (the directory)
size_t bytes_written;
ipfs_merkledag_add(*parent_node, local_node->repo, &bytes_written);
if (file != NULL)
free(file);
if (path != NULL)
free (path);
os_utils_free_file_list(first);
} else {
// process this file
FILE* file = fopen(fileName, "rb");
if (file == 0)
return 0;
retVal = ipfs_hashtable_node_new(parent_node);
if (retVal == 0) {
return 0;
}
// add all nodes (will be called multiple times for large files)
while ( bytes_read == MAX_DATA_SIZE) {
size_t written = 0;
bytes_read = ipfs_import_chunk(file, *parent_node, local_node->repo, &total_size, &written);
*bytes_written += written;
}
fclose(file);
}
// notify the network
struct HashtableNode *htn = *parent_node;
local_node->routing->Provide(local_node->routing, htn->hash, htn->hash_size);
// notify the network of the subnodes too
struct NodeLink *nl = htn->head_link;
while (nl != NULL) {
local_node->routing->Provide(local_node->routing, nl->hash, nl->hash_size);
nl = nl->next;
}
return 1;
}
/**
* Pulls list of files from command line parameters
* @param argc number of command line parameters
* @param argv command line parameters
* @returns a FileList linked list of filenames
*/
struct FileList* ipfs_import_get_filelist(struct CliArguments* args) {
struct FileList* first = NULL;
struct FileList* last = NULL;
for (int i = args->verb_index + 1; i < args->argc; i++) {
if (strcmp(args->argv[i], "add") == 0) {
continue;
}
struct FileList* current = (struct FileList*)malloc(sizeof(struct FileList));
if (current == NULL) {
return NULL;
}
current->next = NULL;
current->file_name = args->argv[i];
// now wire it in
if (first == NULL) {
first = current;
}
if (last != NULL) {
last->next = current;
}
// now set last to current
last = current;
}
return first;
}
/**
* See if the recursive flag was passed on the command line
* @param argc number of command line parameters
* @param argv command line parameters
* @returns true(1) if -r was passed, false(0) otherwise
*/
int ipfs_import_is_recursive(int argc, char** argv) {
for(int i = 0; i < argc; i++) {
if (strcmp(argv[i], "-r") == 0)
return 1;
}
return 0;
}
/**
* called from the command line to import multiple files or directories
* @param argc the number of arguments
* @param argv the arguments
*/
int ipfs_import_files(struct CliArguments* args) {
/*
* Param 0: ipfs
* param 1: add
* param 2: -r (optional)
* param 3: directoryname
*/
struct IpfsNode* local_node = NULL;
char* repo_path = NULL;
int retVal = 0;
struct FileList* first = NULL;
struct FileList* current = NULL;
char* path = NULL;
char* filename = NULL;
struct HashtableNode* directory_entry = NULL;
int recursive = ipfs_import_is_recursive(args->argc, args->argv);
// parse the command line
first = ipfs_import_get_filelist(args);
// open the repo
if (!ipfs_repo_get_directory(args->argc, args->argv, &repo_path)) {
fprintf(stderr, "Repo does not exist: %s\n", repo_path);
goto exit;
}
ipfs_node_offline_new(repo_path, &local_node);
/** disabling for the time being
if (local_node->mode == MODE_API_AVAILABLE) {
// do this through the API
struct HttpRequest* request = ipfs_core_http_request_new();
request->command = "add";
struct HttpParam* recursive_param = ipfs_core_http_param_new();
recursive_param->name = strdup("recursive");
recursive_param->value = strdup((recursive ? "true" : "false"));
libp2p_utils_vector_add(request->params, recursive_param);
current = first;
while (current != NULL) {
libp2p_utils_vector_add(request->arguments, current->file_name);
current = current->next;
}
uint8_t* result = NULL;
size_t result_size = 0;
if (!ipfs_core_http_request_post(local_node, request, &result, &result_size, data, data_size)) {
}
} else {
*/
// No daemon is running. Do this without using the API
// import the file(s)
current = first;
while (current != NULL) {
if (current->file_name[0] != '-') { // not a switch
os_utils_split_filename(current->file_name, &path, &filename);
size_t bytes_written = 0;
if (!ipfs_import_file(NULL, current->file_name, &directory_entry, local_node, &bytes_written, recursive))
goto exit;
ipfs_import_print_node_results(directory_entry, filename);
// cleanup
if (path != NULL) {
free(path);
path = NULL;
}
if (filename != NULL) {
free(filename);
filename = NULL;
}
if (directory_entry != NULL) {
ipfs_hashtable_node_free(directory_entry);
directory_entry = NULL;
}
}
current = current->next;
}
// } uncomment this line when the api is up and running with file transfer
retVal = 1;
exit:
if (local_node != NULL)
ipfs_node_free(local_node);
// free file list
current = first;
while (current != NULL) {
first = current->next;
free(current);
current = first;
}
if (path != NULL)
free(path);
if (filename != NULL)
free(filename);
if (directory_entry != NULL)
ipfs_hashtable_node_free(directory_entry);
//if (repo_path != NULL)
// free(repo_path);
return retVal;
}

View file

@ -1,321 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include "ipfs/importer/resolver.h"
#include "libp2p/utils/logger.h"
#include "libp2p/crypto/encoding/base58.h"
#include "libp2p/conn/session.h"
#include "libp2p/routing/dht_protocol.h"
#include "ipfs/merkledag/node.h"
#include "ipfs/merkledag/merkledag.h"
#include "ipfs/repo/fsrepo/fs_repo.h"
#include "libp2p/net/multistream.h"
#include "libp2p/record/message.h"
#include "multiaddr/multiaddr.h"
#include "libp2p/record/message.h"
#include "libp2p/conn/dialer.h"
/**
* return the next chunk of a path
* @param path the path
* @param next_part a pointer to a string NOTE: don't forget to free
* @returns true(1) on success, false(0) on error, or no more parts
*/
int ipfs_resolver_next_path(const char* path, char** next_part) {
for (int i = 0; i < strlen(path); i++) {
if (path[i] != '/') { // we have the next section
char* pos = strchr(&path[i+1], '/');
if (pos == NULL) {
*next_part = (char*)malloc(strlen(path) + 1);
if ( *next_part == NULL) {
// memory issue
return 0;
}
strcpy(*next_part, path);
} else {
*next_part = (char*)malloc(pos - &path[i] + 1);
if (*next_part == NULL) {
return 0;
}
strncpy(*next_part, &path[i], pos-&path[i]);
(*next_part)[pos-&path[i]] = 0;
}
return 1;
}
}
return 0;
}
/**
* Remove preceding slash and "/ipfs/" or "/ipns/" as well as the local multihash (if it is local)
* @param path the path from the command line
* @param fs_repo the local repo
* @returns the modified path
*/
const char* ipfs_resolver_remove_path_prefix(const char* path, const struct FSRepo* fs_repo) {
int pos = 0;
int first_non_slash = -1;
while(&path[pos] != NULL) {
if (path[pos] == '/') {
pos++;
continue;
} else {
if (first_non_slash == -1)
first_non_slash = pos;
if (pos == first_non_slash && (strncmp(&path[pos], "ipfs", 4) == 0 || strncmp(&path[pos], "ipns", 4) == 0) ) {
// ipfs or ipns should be up front. Otherwise, it could be part of the path
pos += 4;
} else if (strncmp(&path[pos], fs_repo->config->identity->peer->id, fs_repo->config->identity->peer->id_size) == 0) {
pos += fs_repo->config->identity->peer->id_size + 1; // the slash
} else {
return &path[pos];
}
}
}
return NULL;
}
/**
* Determine if this path is a remote path
* @param path the path to examine
* @param fs_repo the local repo
* @returns true(1) if this path is a remote path
*/
int ipfs_resolver_is_remote(const char* path, const struct FSRepo* fs_repo) {
int pos = 0;
// skip the first slash
while (&path[pos] != NULL && path[pos] == '/') {
pos++;
}
if (&path[pos] == NULL)
return 0;
// skip the ipfs prefix
if (strncmp(&path[pos], "ipfs/", 5) == 0 || strncmp(&path[pos], "ipns/", 5) == 0) {
pos += 5; //the word plus the slash
} else
return 0;
// if this is a Qm code, see if it is a local Qm code
if (path[pos] == 'Q' && path[pos+1] == 'm') {
if (strncmp(&path[pos], fs_repo->config->identity->peer->id, fs_repo->config->identity->peer->id_size) != 0) {
return 1;
}
}
return 0;
}
/**
* Retrieve a node from a remote source
* @param path the path to retrieve
* @param from where to start
* @param fs_repo the local repo
* @returns the node, or NULL if not found
*/
struct HashtableNode* ipfs_resolver_remote_get(const char* path, struct HashtableNode* from, const struct IpfsNode* ipfs_node) {
// parse the path
const char* temp = ipfs_resolver_remove_path_prefix(path, ipfs_node->repo);
if (temp == NULL)
return NULL;
char* pos = strchr(temp, '/');
if (pos == NULL || pos - temp > 254)
return NULL;
char id[255];
size_t id_size = pos - temp;
strncpy(id, temp, id_size);
id[id_size] = 0;
char* key = &pos[1];
pos = strchr(key, '/');
if (pos == NULL || pos - key > 254)
return NULL;
pos[0] = '\0';
// get the multiaddress for this
struct Libp2pPeer* peer = libp2p_peerstore_get_peer(ipfs_node->peerstore, (unsigned char*)id, id_size);
if (peer == NULL) {
//TODO: We don't have the peer address. Ask the swarm for the data related to the hash
return NULL;
}
if (!libp2p_peer_connect(ipfs_node->dialer, peer, ipfs_node->peerstore, ipfs_node->repo->config->datastore, 10))
return NULL;
struct Stream* kademlia_stream = libp2p_conn_dialer_get_stream(ipfs_node->dialer, peer, "kademlia");
if (kademlia_stream == NULL)
return NULL;
// build the request
struct KademliaMessage* message = libp2p_message_new();
message->message_type = MESSAGE_TYPE_GET_VALUE;
message->key = key;
message->key_size = strlen(key);
size_t b58size = 100;
uint8_t *b58key = (uint8_t*) malloc(b58size);
if (b58key == NULL) {
libp2p_crypto_encoding_base58_encode((unsigned char*)message->key, message->key_size, (unsigned char**) &b58key, &b58size);
libp2p_logger_debug("resolver", "Attempting to use kademlia to get key %s.\n", b58key);
free(b58key);
}
size_t message_protobuf_size = libp2p_message_protobuf_encode_size(message);
unsigned char message_protobuf[message_protobuf_size];
libp2p_message_protobuf_encode(message, message_protobuf, message_protobuf_size, &message_protobuf_size);
libp2p_message_free(message);
struct StreamMessage outgoing;
outgoing.data = message_protobuf;
outgoing.data_size = message_protobuf_size;
kademlia_stream->write(kademlia_stream->stream_context, &outgoing);
struct StreamMessage* response;
// we should get back a protobuf'd record
kademlia_stream->read(kademlia_stream->stream_context, &response, 5);
if (response->data_size == 1)
return NULL;
// turn the protobuf into a Node
struct HashtableNode* node;
ipfs_hashtable_node_protobuf_decode(response->data, response->data_size, &node);
libp2p_stream_message_free(response);
return node;
}
/**
* Interogate the path and the current node, looking
* for the desired node.
* @param path the current path
* @param from the current node (or NULL if it is the first call)
* @returns what we are looking for, or NULL if it wasn't found
*/
struct HashtableNode* ipfs_resolver_get(const char* path, struct HashtableNode* from, const struct IpfsNode* ipfs_node) {
struct FSRepo* fs_repo = ipfs_node->repo;
// shortcut for remote files
if (from == NULL && ipfs_resolver_is_remote(path, fs_repo)) {
return ipfs_resolver_remote_get(path, from, ipfs_node);
}
/**
* Memory management notes:
* If we find what we're looking for, we clean up "from" and return the object
* If we don't find what we're looking for, but we can continue the search, we clean up "from"
* If we don't find what we're looking for, and we cannot continue, we do not clean up "from"
*/
// remove unnecessary stuff
if (from == NULL)
path = ipfs_resolver_remove_path_prefix(path, fs_repo);
// grab the portion of the path to work with
char* path_section;
if (ipfs_resolver_next_path(path, &path_section) == 0)
return NULL;
struct HashtableNode* current_node = NULL;
if (from == NULL) {
// this is the first time around. Grab the root node
if (path_section[0] == 'Q' && path_section[1] == 'm') {
// we have a hash. Convert to a real hash, and find the node
size_t hash_length = libp2p_crypto_encoding_base58_decode_size(strlen(path_section));
unsigned char hash[hash_length];
unsigned char* ptr = &hash[0];
if (libp2p_crypto_encoding_base58_decode((unsigned char*)path_section, strlen(path_section), &ptr, &hash_length) == 0) {
free(path_section);
return NULL;
}
if (ipfs_merkledag_get_by_multihash(hash, hash_length, &current_node, fs_repo) == 0) {
free(path_section);
return NULL;
}
// we have the root node, now see if we want this or something further down
int pos = strlen(path_section);
if (pos == strlen(path)) {
free(path_section);
return current_node;
} else {
// look on...
free(path_section);
struct HashtableNode* newNode = ipfs_resolver_get(&path[pos+1], current_node, ipfs_node); // the +1 is the slash
return newNode;
}
} else {
// we don't have a current node, and we don't have a hash. Something is wrong
free(path_section);
return NULL;
}
} else {
// we were passed a node. If it is a directory, see if what we're looking for is in it
if (ipfs_hashtable_node_is_directory(from)) {
struct NodeLink* curr_link = from->head_link;
while (curr_link != NULL) {
// if it matches the name, we found what we're looking for.
// If so, load up the node by its hash
if (strcmp(curr_link->name, path_section) == 0) {
if (ipfs_merkledag_get(curr_link->hash, curr_link->hash_size, &current_node, fs_repo) == 0) {
free(path_section);
return NULL;
}
if (strlen(path_section) == strlen(path)) {
// we are at the end of our search
ipfs_hashtable_node_free(from);
from = NULL;
free(path_section);
return current_node;
} else {
char* next_path_section;
ipfs_resolver_next_path(&path[strlen(path_section)], &next_path_section);
free(path_section);
// if we're at the end of the path, return the node
// continue looking for the next part of the path
ipfs_hashtable_node_free(from);
from = NULL;
struct HashtableNode* newNode = ipfs_resolver_get(next_path_section, current_node, ipfs_node);
return newNode;
}
}
curr_link = curr_link->next;
}
} else {
// we're asking for a file from an object that is not a directory. Bail.
free(path_section);
return NULL;
}
}
// it should never get here
free(path_section);
if (from != NULL)
ipfs_hashtable_node_free(from);
return NULL;
}
/**
* Interrogate the path, looking for the peer.
* NOTE: only called locally. Not for remote callers
* @param path the peer path to search for in the form like "/ipfs/QmKioji..."
* @param ipfs_node the context
* @returns a peer struct, or NULL if not found
*/
struct Libp2pPeer* ipfs_resolver_find_peer(const char* path, const struct IpfsNode* ipfs_node) {
struct FSRepo* fs_repo = ipfs_node->repo;
struct Libp2pLinkedList *addresses = NULL;
struct Libp2pPeer* peer = NULL;
// shortcut for if this node is the node we're looking for
if (!ipfs_resolver_is_remote(path, fs_repo)) {
// turn the string list into a multiaddress list
struct Libp2pLinkedList* current_list_string = fs_repo->config->addresses->swarm_head;
struct Libp2pLinkedList* current_list_ma = addresses;
while(current_list_string != NULL) {
struct Libp2pLinkedList* item = libp2p_utils_linked_list_new();
item->item = multiaddress_new_from_string(current_list_string->item);
if (addresses == NULL) {
addresses = item;
} else {
current_list_ma->next = item;
}
current_list_ma = item;
current_list_string = current_list_string->next;
}
}
// ask the swarm for the peer
const char* address_string = ipfs_resolver_remove_path_prefix(path, fs_repo);
ipfs_node->routing->FindPeer(ipfs_node->routing, (const unsigned char*)address_string, strlen(address_string), &peer);
return peer;
}

View file

@ -1,6 +1,5 @@
/***
* IPFS has the notion of storage blocks.
* Raw data with a multihash key (the Cid)
*/
#ifndef __IPFS_BLOCKS_BLOCK_H__
@ -15,51 +14,19 @@ struct Block {
};
/***
* Create a new block
* @returns a new allocated Block struct
* Create a new block based on the incoming data.
* @param data the data to base the block on
* @param data_size the length of the data array
* @param block a pointer to the struct Block that will be created
* @returns true(1) on success
*/
struct Block* ipfs_block_new();
int ipfs_blocks_block_add_data(const unsigned char* data, size_t data_size, struct Block* block);
int ipfs_blocks_block_new(const unsigned char* data, size_t data_size, struct Block** block);
/***
* Free resources used by the creation of a block
* @param block the block to free
* @returns true(1) on success
*/
int ipfs_block_free(struct Block* block);
/**
* Determine the approximate size of an encoded block
* @param block the block to measure
* @returns the approximate size needed to encode the protobuf
*/
size_t ipfs_blocks_block_protobuf_encode_size(const struct Block* block);
/**
* Encode the Block into protobuf format
* @param block the block to encode
* @param buffer the buffer to fill
* @param max_buffer_size the max size of the buffer
* @param bytes_written the number of bytes used
* @returns true(1) on success
*/
int ipfs_blocks_block_protobuf_encode(const struct Block* block, unsigned char* buffer, size_t max_buffer_size, size_t* bytes_written);
/***
* Decode from a protobuf stream into a Block struct
* @param buffer the buffer to pull from
* @param buffer_length the length of the buffer
* @param block the block to fill
* @returns true(1) on success
*/
int ipfs_blocks_block_protobuf_decode(const unsigned char* buffer, const size_t buffer_length, struct Block** block);
/***
* Make a copy of a block
* @param original the original
* @returns a new Block that is a copy
*/
struct Block* ipfs_block_copy(struct Block* original);
int ipfs_blocks_block_free(struct Block* block);
#endif

View file

@ -3,95 +3,36 @@
*/
#ifndef __IPFS_BLOCKS_BLOCKSTORE_H__
#define __IPFS_BLOCKS_BLOCKSTORE_H__
#include "ipfs/cid/cid.h"
#include "ipfs/repo/fsrepo/fs_repo.h"
struct BlockstoreContext {
const struct FSRepo* fs_repo;
};
struct Blockstore {
struct BlockstoreContext* blockstoreContext;
int (*Delete)(const struct BlockstoreContext* context, struct Cid* cid);
int (*Has)(const struct BlockstoreContext* context, struct Cid* cid);
/**
* Retrieve a block from the blockstore
*/
int (*Get)(const struct BlockstoreContext* context, struct Cid* cid, struct Block** block);
int (*Put)(const struct BlockstoreContext* context, struct Block* block, size_t* bytes_written);
};
/***
* Create a new Blockstore struct
* @param fs_repo the FSRepo to use
* @returns the new Blockstore struct, or NULL if there was a problem.
*/
struct Blockstore* ipfs_blockstore_new(const struct FSRepo* fs_repo);
/**
* Release resources of a Blockstore struct
* @param blockstore the struct to free
* @returns true(1)
*/
int ipfs_blockstore_free(struct Blockstore* blockstore);
#ifndef __IPFS_BLOCKS_BLOCKSTORE_H__
/**
* Delete a block based on its Cid
* @param context the context
* @param cid the Cid to look for
* @param returns true(1) on success
*/
int ipfs_blockstore_delete(const struct BlockstoreContext* context, struct Cid* cid);
int ipfs_blockstore_delete(struct Cid* cid, struct FSRepo* fs_repo);
/***
* Determine if the Cid can be found
* @param context the context
* @param cid the Cid to look for
* @returns true(1) if found
*/
int ipfs_blockstore_has(const struct BlockstoreContext* context, struct Cid* cid);
int ipfs_blockstore_has(struct Cid* cid, struct FSRepo* fs_repo);
/***
* Find a block based on its Cid
* @param context the context
* @param cid the Cid to look for
* @param block where to put the data to be returned
* @returns true(1) on success
*/
int ipfs_blockstore_get(const struct BlockstoreContext* context, struct Cid* cid, struct Block** block);
int ipfs_blockstore_get(struct Cid* cid, struct Block* block, struct FSRepo* fs_repo);
/***
* Put a block in the blockstore
* @param block the block to store
* @returns true(1) on success
*/
int ipfs_blockstore_put(const struct BlockstoreContext* context, struct Block* block, size_t* bytes_written);
int ipfs_blockstore_put(struct Block* block, struct FSRepo* fs_repo);
/***
* Put a struct UnixFS in the blockstore
* @param unix_fs the structure
* @param fs_repo the repo to place the strucure in
* @param bytes_written the number of bytes written to the blockstore
* @returns true(1) on success
*/
int ipfs_blockstore_put_unixfs(const struct UnixFS* unix_fs, const struct FSRepo* fs_repo, size_t* bytes_written);
/***
* Find a UnixFS struct based on its hash
* @param hash the hash to look for
* @param hash_length the length of the hash
* @param unix_fs the struct to fill
* @param fs_repo where to look for the data
* @returns true(1) on success
*/
int ipfs_blockstore_get_unixfs(const unsigned char* hash, size_t hash_length, struct UnixFS** block, const struct FSRepo* fs_repo);
/**
* Put a struct Node in the blockstore
*/
int ipfs_blockstore_put_node(const struct HashtableNode* node, const struct FSRepo* fs_repo, size_t* bytes_written);
int ipfs_blockstore_get_node(const unsigned char* hash, size_t hash_length, struct HashtableNode** node, const struct FSRepo* fs_repo);
#endif

View file

@ -6,82 +6,36 @@
#define __IPFS_CID_CID_H
#include <stddef.h>
#include "protobuf.h"
// these are multicodec packed content types. They should match
// the codes described in the authoratative document:
// https://github.com/multiformats/multicodec/blob/master/table.csv
#define CID_RAW 0x55
#define CID_DAG_PROTOBUF 0x70
#define CID_DAG_CBOR 0x71
#define CID_GIT_RAW 0x78
#define CID_PROTOBUF 0x70
#define CID_CBOR 0x71
#define CID_RAW 0x72
#define CID_JSON 0x73
#define CID_ETHEREUM_BLOCK 0x90
#define CID_ETHEREUM_BLOCKLIST 0x91
#define CID_ETHEREUM_TRIE 0x92
#define CID_ETHEREUM_TX 0x93
#define CID_ETHEREUM_TX_RECEIPT_TRIE 0x94
#define CID_ETHEREUM_TX_RECEIPT 0x95
#define CID_ETHEREUM_STATE_TRIE 0x96
#define CID_ETHEREUM_ACCOUNT_SNAPSHOT 0x97
#define CID_ETHEREUM_STORAGE_TRIE 0x98
#define CID_ETHEREUM_TX 0x91
#define CID_BITCOIN_BLOCK 0xb0
#define CID_BITCOIN_TX 0xb1
#define CID_ZCASH_BLOCK 0xc0
#define CID_ZCASH_TX 0xc1
/***
* A note about CID versions:
* Version 0 only contained the multihash address. The extra parameters of multibase,
* multicodec, cid-version were implied (base58btc, protobuf-mdag, and cidv0
* respectively) are implied.
*/
struct Cid {
int version; // CID version
int codec; // codec used (i.e. CID_RAW, CID_PROTOBUF
unsigned char* hash; // the multihash
size_t hash_length; // the length of hash
int version;
char codec;
unsigned char* hash; // a multihash
size_t hash_length;
};
struct CidSet {
struct Cid *cid;
struct CidSet *next;
};
/***
* encode a Cid into a protobuf array of bytes
* @param incoming the incoming Cid struct
* @param buffer the buffer
* @param max_buffer_length the length of the buffer
* @param bytes_written the number of bytes written
*/
int ipfs_cid_protobuf_encode(const struct Cid* incoming, unsigned char* buffer, size_t max_buffer_length, size_t* bytes_written);
/***
* decode an array of bytes into a Cid structure
* @param buffer the incming array of bytes
* @param buffer_length the length of the buffer
* @param output the Cid struct NOTE: all allocations are made by this function. Be sure to call free
* @returns true(1) on success
*/
int ipfs_cid_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct Cid** output);
/***
* Returns a safe estimate of the required buffer size to encode the Cid struct
* @param incoming the struct to encode
* @returns the number of approximate bytes
*/
size_t ipfs_cid_protobuf_encode_size(const struct Cid* incoming);
/**
* Create a new CID based on the given hash
* @param version the version
* @param hash the multihash
* @param hash_length the length of the multihash in bytes
* @param codec the codec to be used (NOTE: For version 0, this should be CID_DAG_PROTOBUF)
* @returns the Cid, or NULL if there was a problem
* @param codec the codec to be used (NOTE: For version 0, this should be CID_PROTOBUF)
* @param cid where to put the results
* @returns true(1) on success
*/
struct Cid* ipfs_cid_new(int version, const unsigned char* hash, size_t hash_length, const char codec);
int ipfs_cid_new(int version, unsigned char* hash, size_t hash_length, const char codec, struct Cid** cid);
/***
* Free the resources from a Cid
@ -90,13 +44,6 @@ struct Cid* ipfs_cid_new(int version, const unsigned char* hash, size_t hash_len
*/
int ipfs_cid_free(struct Cid* cid);
/***
* Make a copy of a Cid
* @param original the original
* @returns a copy of the original
*/
struct Cid* ipfs_cid_copy(const struct Cid* original);
/***
* Fill a Cid struct based on a base 58 encoded string
* @param incoming the string
@ -104,32 +51,7 @@ struct Cid* ipfs_cid_copy(const struct Cid* original);
* @cid the Cid struct to fill
* @return true(1) on success
*/
int ipfs_cid_decode_hash_from_base58(const unsigned char* incoming, size_t incoming_length, struct Cid** cid);
/***
* Create a CID from an ipfs or ipns string (i.e. "/ipns/QmAb12CD..."
* @param incoming the incoming string
* @param cid the resultant Cid
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_cid_decode_hash_from_ipfs_ipns_string(const char* incoming, struct Cid** cid);
/**
* Turn a cid into a base 58 of a multihash of the cid hash
* @param cid the cid to work with
* @param buffer where to put the results
* @param max_buffer_length the maximum space reserved for the results
* @returns true(1) on success
*/
int ipfs_cid_hash_to_base58(const unsigned char* hash, size_t hash_length, unsigned char* buffer, size_t max_buffer_length);
/***
* Turn the hash of this CID into a c string
* @param cid the cid
* @param result a place to allocate and store the string
* @returns a pointer to the string (*result) or NULL if there was a problem
*/
char* ipfs_cid_to_string(const struct Cid* cid, char **result);
int ipfs_cid_decode_from_string(const unsigned char* incoming, size_t incoming_length, struct Cid** cid);
/***
* Turn a multibase decoded string of bytes into a Cid struct
@ -137,23 +59,6 @@ char* ipfs_cid_to_string(const struct Cid* cid, char **result);
* @param incoming_size the size of the array
* @param cid the Cid structure to fill
*/
int ipfs_cid_cast(const unsigned char* incoming, size_t incoming_size, struct Cid* cid);
struct CidSet *ipfs_cid_set_new ();
void ipfs_cid_set_destroy (struct CidSet **set);
int ipfs_cid_set_add (struct CidSet *set, struct Cid *cid, int visit);
int ipfs_cid_set_has (struct CidSet *set, struct Cid *cid);
int ipfs_cid_set_remove (struct CidSet *set, struct Cid *cid);
int ipfs_cid_set_len (struct CidSet *set);
unsigned char **ipfs_cid_set_keys (struct CidSet *set);
int ipfs_cid_set_foreach (struct CidSet *set, int (*func)(struct Cid *));
/**
* Compare two cids
* @param a side A
* @param b side B
* @returns < 0 if side A is greater, > 0 if side B is greater, or 0 if equal
*/
int ipfs_cid_compare(const struct Cid* a, const struct Cid* b);
int ipfs_cid_cast(unsigned char* incoming, size_t incoming_size, struct Cid* cid);
#endif

View file

@ -1,19 +0,0 @@
#pragma once
/**
* Helps parse the command line.
*/
/**
* A structure to hold the command line arguments
*/
struct CliArguments {
int argc;
char** argv;
int verb_index;
char* config_dir;
};
struct CliArguments* cli_arguments_new(int argc, char** argv);
void cli_arguments_free(struct CliArguments* args);

View file

@ -20,12 +20,4 @@ int ipfs_cmd_ipfs_init_command_new(struct Command* command);
*/
int ipfs_cmd_ipfs_init_command_free(struct Command* command);
/***
* Parse the command line
* @param argc the number of arguments
* @param argv the actual arguments
* @returns a command structure
*/
struct Command* ipfs_cmd_parse_command_line(int argc, char** argv);
#endif

View file

@ -16,7 +16,7 @@
* @param request the end result, something that can be passed on that actually does something
* @returns 0 if something bad happens, otherwise 1
*/
int cli_parse(int argc, char** params, FILE* inStream, struct Command** cmd, struct Request* request);
int cli_parse(char** params, FILE* inStream, struct Command* cmd, struct Request* request);
int cli_parse_opts(char** params, struct Command* cmd, char* path, char** stringVals);

View file

@ -1,3 +1,11 @@
//
// option.h
// c-ipfs
//
// Created by John Jones on 10/26/16.
// Copyright © 2016 JMJAtlanta. All rights reserved.
//
#ifndef __COMMANDS_COMMAND_OPTION_H__
#define __COMMANDS_COMMAND_OPTION_H__

View file

@ -1,3 +1,11 @@
//
// context.h
// c-ipfs
//
// Created by John Jones on 10/27/16.
// Copyright © 2016 JMJAtlanta. All rights reserved.
//
#ifndef __COMMANDS_CONTEXT_H__
#define __COMMANDS_CONTEXT_H__

View file

@ -1,3 +1,11 @@
//
// req_log.h
// c-ipfs
//
// Created by John Jones on 10/27/16.
// Copyright © 2016 JMJAtlanta. All rights reserved.
//
#ifndef __COMMANDS_REQ_LOG_H__
#define __COMMANDS_REQ_LOG_H__

View file

@ -1,3 +1,11 @@
//
// request.h
// c-ipfs
//
// Created by John Jones on 10/26/16.
// Copyright © 2016 JMJAtlanta. All rights reserved.
//
#ifndef __COMMANDS_REQUEST_H__
#define __COMMANDS_REQUEST_H__

View file

@ -1,95 +0,0 @@
#pragma once
#include <pthread.h>
#include "ipfs/core/ipfs_node.h"
#ifdef __x86_64__
#define INT_TYPE uint64_t
#else
#define INT_TYPE uint32_t
#endif
#define MAX_READ (32*1024) // 32k
#define MAX_CHUNK (32*1024) // 32k
struct ApiContext {
int socket;
uint32_t ipv4;
uint16_t port;
int max_conns;
int timeout;
pthread_mutex_t conns_lock;
int conns_count;
pthread_t api_thread;
struct s_conns {
int socket;
uint32_t ipv4;
uint16_t port;
pthread_t pthread;
} **conns;
};
struct s_request {
char *buf;
size_t size;
int method;
int path;
int request;
int query;
int http_ver;
int header;
int body;
size_t body_size;
int boundary;
size_t boundary_size;
};
#define API_V0_START "/api/v0/"
#define WEBUI_ADDR "/ipfs/QmPhnvn747LqwPYMJmQVorMaGbMSgA7mRRoyyZYz3DoZRQ/"
#define HTTP_301 "HTTP/1.1 301 Moved Permanently\r\n" \
"Location: %s\r\n" \
"Content-Type: text/html\r\n\r\n" \
"<a href=\"%s\">Moved Permanently</a>.\r\n\r\n"
#define HTTP_302 "HTTP/1.1 302 Found\r\n" \
"Content-Type: text/html\r\n" \
"Connection: close\r\n" \
"Location: %s\r\n" \
"X-Ipfs-Path: %s\r\n\r\n" \
"<a href=\"%s\">Found</a>.\r\n\r\n"
#define HTTP_400 "HTTP/1.1 400 Bad Request\r\n" \
"Content-Type: text/plain\r\n" \
"Connection: close\r\n\r\n" \
"400 Bad Request"
#define HTTP_404 "HTTP/1.1 404 Not Found\r\n" \
"Content-Type: text/plain\r\n" \
"Connection: close\r\n\r\n" \
"404 page not found"
#define HTTP_500 "HTTP/1.1 500 Internal server error\r\n" \
"Content-Type: text/plain\r\n" \
"Connection: close\r\n\r\n" \
"500 Internal server error"
#define HTTP_501 "HTTP/1.1 501 Not Implemented\r\n" \
"Content-Type: text/plain\r\n" \
"Connection: close\r\n\r\n" \
"501 Not Implemented"
#define write_cstr(f,s) write(f,s,sizeof(s)-1)
#define write_str(f,s) write(f,s,strlen(s))
#define cstrstart(a,b) (memcmp(a,b,sizeof(b)-1)==0)
#define strstart(a,b) (memcmp(a,b,strlen(b))==0)
int api_send_resp_chunks(int fd, void *buf, size_t size);
void *api_connection_thread (void *ptr);
void api_connections_cleanup (struct IpfsNode* node);
void *api_listen_thread (void *ptr);
int api_start (struct IpfsNode* local_node, int max_conns, int timeout);
int api_stop (struct IpfsNode* local_node);

View file

@ -1,14 +0,0 @@
#pragma once
/**
* Fires up the connection to peers
*/
/**
* Connect to the peers in the config file
* @param param a IpfsNode object
* @returns nothing useful
*/
void *ipfs_bootstrap_swarm(void* param);
void *ipfs_bootstrap_routing(void* param);

View file

@ -1,3 +1,11 @@
//
// builder.h
// c-ipfs
//
// Created by John Jones on 10/27/16.
// Copyright © 2016 JMJAtlanta. All rights reserved.
//
#ifndef __CORE_BUILDER_H__
#define __CORE_BUILDER_H__
@ -5,7 +13,6 @@
#include "ipfs/commands/context.h"
#include "ipfs/repo/config/config.h"
#include "ipfs/core/ipfs_node.h"
struct BuildCfg {
int online;

View file

@ -1,8 +0,0 @@
#include "ipfs/core/ipfs_node.h"
/**
* Determine if the API is running
* @param local_node the context
* @returns true(1) on success, false(0) otherwise
*/
int api_running(struct IpfsNode* local_node);

View file

@ -1,34 +0,0 @@
#ifndef DAEMON_H
#define DAEMON_H
#include <stdint.h>
#include "ipfs/core/ipfs_node.h"
#define MAX 5
#define CONNECTIONS 50
struct null_connection_params {
int file_descriptor;
int *count;
char* ip;
int port;
struct IpfsNode* local_node;
};
struct null_listen_params {
uint32_t ipv4;
uint16_t port;
};
struct IpfsNodeListenParams {
uint32_t ipv4;
uint16_t port;
struct IpfsNode* local_node;
};
int ipfs_daemon (int argc, char **argv);
int ipfs_daemon_start(char* repo_path);
int ipfs_daemon_stop();
int ipfs_ping (int argc, char **argv);
#endif // DAEMON_H

View file

@ -1,89 +0,0 @@
#pragma once
#include "ipfs/core/ipfs_node.h"
/***
* A name/value pair of http parameters
*/
struct HttpParam {
char* name; // the name of the parameter
char* value; // the value of the parameter
};
/**
* A struct to help with incoming http requests
*/
struct HttpRequest {
char* command; // the command
char* sub_command; // the sub command
struct Libp2pVector* params; // a collection of HttpParam structs
struct Libp2pVector* arguments; // a collection of chars that are arguments
};
/***
* A struct to hold the response to be sent via http
*/
struct HttpResponse {
char* content_type; // a const char, not dynamically allocated
uint8_t* bytes; // dynamically allocated
size_t bytes_size;
};
/***
* Build a new HttpRequest
* @returns the newly allocated HttpRequest struct
*/
struct HttpRequest* ipfs_core_http_request_new();
/***
* Clean up resources of a HttpRequest struct
* @param request the struct to destroy
*/
void ipfs_core_http_request_free(struct HttpRequest* request);
struct HttpResponse* ipfs_core_http_response_new();
void ipfs_core_http_response_free(struct HttpResponse* response);
/***
* Build a new HttpParam
* @returns a newly allocated HttpParam struct
*/
struct HttpParam* ipfs_core_http_param_new();
/***
* Clean up resources allocated by a HttpParam struct
* @param param the struct to destroy
*/
void ipfs_core_http_param_free(struct HttpParam* param);
/***
* Process the parameters passed in from an http request
* @param local_node the context
* @param request the request
* @param response the response
* @returns true(1) on success, false(0) otherwise.
*/
int ipfs_core_http_request_process(struct IpfsNode* local_node, struct HttpRequest* request, struct HttpResponse** response);
/**
* Do an HTTP Get to the local API
* @param local_node the context
* @param request the request
* @param result the results
* @param result_size the size of the results
* @returns true(1) on success, false(0) on error
*/
int ipfs_core_http_request_get(struct IpfsNode* local_node, struct HttpRequest* request, char** result, size_t* result_size);
/**
* Do an HTTP Post to the local API
* @param local_node the context
* @param request the request
* @param result the results
* @param result_size the size of the results
* @param data the array with post data
* @param data_size the data length
* @returns true(1) on success, false(0) on error
*/
int ipfs_core_http_request_post(struct IpfsNode* local_node, struct HttpRequest* request, char** result, size_t* result_size, char *data, size_t data_size);

View file

@ -1,70 +1,21 @@
#pragma once
//
// ipfs_node.h
// c-ipfs
//
// Created by John Jones on 10/27/16.
// Copyright © 2016 JMJAtlanta. All rights reserved.
//
#include <pthread.h>
#include "libp2p/peer/peerstore.h"
#include "libp2p/peer/providerstore.h"
#include "libp2p/swarm/swarm.h"
#include "ipfs/blocks/blockstore.h"
#include "ipfs/exchange/exchange.h"
#include "ipfs/repo/config/identity.h"
#include "ipfs/repo/fsrepo/fs_repo.h"
#include "ipfs/routing/routing.h"
/***
* Holds information about the local node
*/
/***
* Modes:
* MODE_OFFLINE: Do everything yourself
* MODE_API_AVAILABLE: If you want to, the API is running
* MODE_ONLINE: You are the API
*/
enum NodeMode { MODE_OFFLINE, MODE_API_AVAILABLE, MODE_ONLINE };
#ifndef __CORE_IPFS_NODE_H__
#define __CORE_IPFS_NODE_H__
struct IpfsNode {
/***
* Modes:
* MODE_OFFLINE: Do everything yourself
* MODE_API_AVAILABLE: If you want to, the API is running
* MODE_ONLINE: You are the API
*/
enum NodeMode mode;
struct Identity* identity;
struct FSRepo* repo;
struct Peerstore* peerstore;
struct ProviderStore* providerstore;
struct IpfsRouting* routing;
struct Blockstore* blockstore;
struct Exchange* exchange;
struct Libp2pVector* protocol_handlers;
struct ApiContext* api_context;
struct Dialer* dialer;
struct SwarmContext* swarm;
//struct PeerId identity;
//struct Repo repo;
//struct Pinner pinning; // an interface
//struct Mount** mounts;
//struct PrivKey* private_key;
// TODO: Add more here
};
/***
* build an online IpfsNode
* @param repo_path where the IPFS repository directory is
* @param node the completed IpfsNode struct
* @returns true(1) on success
*/
int ipfs_node_online_new(const char* repo_path, struct IpfsNode** node);
/***
* build an offline IpfsNode
* @param repo_path where the IPFS repository directory is
* @param node the completed IpfsNode struct
* @returns true(1) on success
*/
int ipfs_node_offline_new(const char* repo_path, struct IpfsNode** node);
/***
* Free resources from the creation of an IpfsNode
* @param node the node to free
* @returns true(1)
*/
int ipfs_node_free(struct IpfsNode* node);
#endif /* ipfs_node_h */

View file

@ -1,35 +0,0 @@
#pragma once
#include "libp2p/net/stream.h"
struct IpfsListener {
char* conCh;
char* protocol;
};
/**
* Do a socket accept
* @param listener the listener
* @param stream the returned stream
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_core_net_accept(struct IpfsListener* listener, struct Stream* stream);
/**
* Listen using a particular protocol
* @param node the node
* @param protocol the protocol to use
* @param listener the results
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_core_net_listen(struct IpfsNode* node, char* protocol, struct IpfsListener* listener);
/***
* Dial a peer
* @param node this node
* @param peer_id who to dial
* @param protocol the protocol to use
* @param stream the resultant stream
* @returns true(1) on success, otherwise false(0)
*/
int ipsf_core_net_dial(struct IpfsNode* node, char* peer_id, char* protocol, struct Stream* stream);

View file

@ -1,19 +0,0 @@
#pragma once
#include "libp2p/conn/session.h"
#include "ipfs/core/ipfs_node.h"
void *ipfs_null_connection (void *ptr);
void *ipfs_null_listen (void *ptr);
int ipfs_null_shutdown();
/***
* Handle the incoming request from a Multistream
* @param incoming the incoming request
* @param incoming_size the size of the request in bytes
* @param session the session context
* @param connection_param the connection parameters
* @returns True(1) on success, False(0) on error
*/
int ipfs_multistream_marshal(const unsigned char* incoming, size_t incoming_size, struct SessionContext* session, struct IpfsNode* local_node);

View file

@ -1,8 +0,0 @@
#pragma once
#include "ipfs/cmd/cli.h"
/***
* Handle command line swarm call
*/
int ipfs_swarm (struct CliArguments* args);

View file

@ -1,12 +1,10 @@
#pragma once
/**
* Some code to help with the datastore / blockstore interface
*/
#ifndef __IPFS_DATASTORE_DS_HELPER_H__
#define __IPFS_DATASTORE_DS_HELPER_H__
#include <string.h>
#include "ipfs/blocks/block.h"
#include "libp2p/db/datastore.h"
/**
* Generate a key based on the passed in binary_array
@ -17,8 +15,8 @@
* @param results_length the length of the generated key
* @returns true(1) on success
*/
int ipfs_datastore_helper_ds_key_from_binary(const unsigned char* binary_array, size_t array_length,
unsigned char* results, size_t max_results_length, size_t* results_length);
int ipfs_datastore_helper_ds_key_from_binary(unsigned char* binary_array, size_t array_length,
char* results, size_t max_results_length, size_t* results_length);
/**
* Generate a binary array based on the passed in datastore key
@ -29,13 +27,7 @@ int ipfs_datastore_helper_ds_key_from_binary(const unsigned char* binary_array,
* @param completed_binary_array_length the length of what was written to the binary_array
* @returns true(1) on success
*/
int ipfs_datastore_helper_binary_from_ds_key(const unsigned char* ds_key, size_t key_length, unsigned char* binary_array,
int ipfs_datastore_helper_binary_from_ds_key(unsigned char* ds_key, size_t key_length, unsigned char* binary_array,
size_t max_binary_array_length, size_t* completed_binary_array_length);
/***
* Add a record in the datastore based on a block
* @param block the block
* @param datastore the Datastore
* @reutrns true(1) on success, false(0) otherwise
*/
int ipfs_datastore_helper_add_block_to_datastore(struct Block* block, struct Datastore* datastore);
#endif

View file

@ -1,27 +0,0 @@
#ifndef DNSLINK_H
#define DNSLINK_H
#include "ipfs/util/errs.h"
// DefaultDepthLimit controls how many dns links to resolve through before
// returning. Users can override this default.
#ifndef DefaultDepthLimit
#define DefaultDepthLimit 16
#endif
// MaximumDepthLimit governs the max number of recursive resolutions.
#ifndef MaximumDepthLimit
#define MaximumDepthLimit 256
#endif
#ifndef IPFS_DNSLINK_C
extern int (*ipfs_dnslink_lookup_txt)(char ***, char *);
#endif // IPFS_DNSLINK_C
int ipfs_dns (int argc, char **argv);
int ipfs_dnslink_resolve (char **p, char *domain);
int ipfs_dnslink_resolve_n (char **p, char *d, int depth);
int ipfs_dnslink_resolv_lookupTXT(char ***txt, char *domain);
int ipfs_dnslink_resolve_once (char ***p, char *domain);
int ipfs_dnslink_parse_txt (char **path, char *txt);
int ipfs_dnslink_parse_link_domain (char **domain, char**rest, char *txt);
#endif // DNSLINK_H

View file

@ -1,89 +0,0 @@
#pragma once
/***
* Bitswap implements the "exchange" and "Libp2pProtocolHandler" interfaces
* @see ../exchange.h
* @see libp2p/net/protocol.h
*/
#include "libp2p/net/protocol.h"
#include "ipfs/core/ipfs_node.h"
#include "ipfs/exchange/exchange.h"
#include "ipfs/exchange/bitswap/engine.h"
#include "ipfs/exchange/bitswap/wantlist_queue.h"
struct Libp2pProtocolHandler* ipfs_bitswap_build_protocol_handler(const struct IpfsNode* local_node);
struct BitswapContext {
struct IpfsNode* ipfsNode;
struct WantListQueue* localWantlist;
struct PeerRequestQueue* peerRequestQueue;
struct BitswapEngine* bitswap_engine;
};
/**
* Start up the bitswap exchange
* @param ipfsNode the context
* @returns an Exchange struct that refers to the exchange
*/
struct Exchange* ipfs_bitswap_new(struct IpfsNode* ipfsNode);
/***
* These are the implementation methods for the exchange "Interface"
*/
/***
* Checks to see if the Bitswap service is online
* @param exhcnageContext a pointer to a BitswapContext
* @reutrns true(1) if online, false(0) otherwise.
*/
int ipfs_bitswap_is_online(struct Exchange* exchange);
/***
* Closes down the Bitswap network
* @param exchangeContext a pointer to a BitswapContext
* @returns true(1)
*/
int ipfs_bitswap_close(struct Exchange* exchange);
/****
* Notify the BitswapNetwork that we have this block
* @param exchangeContext a pointer to a BitswapContext
* @block the block that we have
* @reutrns true(1) if successful, false(0) if not.
*/
int ipfs_bitswap_has_block(struct Exchange* exchange, struct Block* block);
/**
* Retrieve a block from the BitswapNetwork
* Note: This may pull the file from the local blockstore.
* Note: If false(0) is returned, block will be NULL
*
* @param exchangeContext a pointer to a BitswapContext
* @param cid the Cid of the block we're looking for
* @param block a pointer to the block when we find it.
* @returns true(1) on success, false(0) otherwise.
*/
int ipfs_bitswap_get_block(struct Exchange* exchange, struct Cid* cid, struct Block** block);
/**
* Retrieve a block from the BitswapNetwork
*
* @param exchangeContext a pointer to a BitswapContext
* @param cid the Cid of the block we're looking for
* @param queue a pointer to the queue that will change if the block arrives
* @returns true(1) on success, false(0) otherwise.
*/
int ipfs_bitswap_get_block_async(struct Exchange* exchange, struct Cid* cid, struct Block** block);
/***
* Retrieve a collection of blocks from the BitswapNetwork
* Note: The return of false(0) means that not all blocks were found.
*
* @param exchangeContext a pointer to a BitswapContext
* @param cids a collection of Cid structs
* @param blocks a collection that contains the results.
* @param true(1) on success, false(0) otherwise
*/
int ipfs_bitswap_get_blocks(struct Exchange* exchange, struct Libp2pVector* cids, struct Libp2pVector** blocks);

View file

@ -1,42 +0,0 @@
#pragma once
#include <pthread.h>
//#include "ipfs/exchange/bitswap/bitswap.h" we must forward declare here, as BitswapContext has a reference to BitswapEngine
struct BitswapContext;
struct BitswapEngine {
int shutting_down;
pthread_t wantlist_processor_thread;
pthread_t peer_request_processor_thread;
};
/***
* Allocate resources for a BitswapEngine
* @returns a new struct BitswapEngine
*/
struct BitswapEngine* ipfs_bitswap_engine_new();
/***
* Deallocate resources from struct BitswapEngine
* @param engine the engine to free
* @returns true(1)
*/
int ipfs_bitswap_engine_free(struct BitswapEngine* engine);
/**
* Starts the bitswap engine that processes queue items. There
* should only be one of these per ipfs instance.
*
* @param context the context
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_bitswap_engine_start(const struct BitswapContext* context);
/***
* Shut down the engine
*
* @param context the context
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_bitswap_engine_stop(const struct BitswapContext* context);

View file

@ -1,218 +0,0 @@
#pragma once
/***
* A protobuf-able Bitswap Message
*/
#include <stdint.h>
#include <stddef.h>
#include "libp2p/utils/vector.h"
struct WantlistEntry {
// optional string block = 1, the block cid (cidV0 in bitswap 1.0.0, cidV1 in bitswap 1.1.0
unsigned char* block;
size_t block_size;
// optional int32 priority = 2, the priority (normalized). default to 1
uint32_t priority;
// optional bool cancel = 3, whether this revokes an entry
uint8_t cancel;
};
struct BitswapWantlist {
// repeated WantlistEntry entries = 1, a list of wantlist entries
struct Libp2pVector* entries;
// optional bool full = 2, whether this is the full wantlist. default to false
uint8_t full;
};
struct BitswapBlock {
// optional bytes prefix = 1, // CID prefix (cid version, multicodec, and multihash prefix (type + length))
uint8_t* prefix;
size_t prefix_size;
// optional bytes data = 2
uint8_t* bytes;
size_t bytes_size;
};
struct BitswapMessage {
// optional Wantlist wantlist = 1
struct BitswapWantlist* wantlist;
// repeated bytes blocks = 2, used to send Blocks in bitswap 1.0.0
struct Libp2pVector* blocks;
// repeated Block payload = 3, used to send Blocks in bitswap 1.1.0
struct Libp2pVector* payload;
};
/***
* Allocate memory for a struct BitswapBlock
* @returns a new BitswapBlock
*/
struct BitswapBlock* ipfs_bitswap_block_new();
/**
* Deallocate memory for a struct BitswapBlock
* @param block the block to deallocate
* @returns true(1)
*/
int ipfs_bitswap_block_free(struct BitswapBlock* block);
/**
* Retrieve an estimate of the size of a protobuf'd BitswapBlock
* @returns the approximate (maximum actually) size of a protobuf'd BitswapBlock
*/
size_t ipfs_bitswap_message_block_protobuf_size(struct BitswapBlock* block);
/***
* Encode a BitswapBlock
* @param incoming the block to encode
* @param outgoing where to place the results
* @param max_size the maximum allocated space for outgoing
* @param bytes_written the number of bytes written to outgoing
*/
int ipfs_bitswap_message_block_protobuf_encode(struct BitswapBlock* incoming, uint8_t* outgoing, size_t max_size, size_t* bytes_written);
/***
* Decode a protobuf to a BitswapBlock
* @param buffer the incoming protobuf
* @param buffer_length the length of the incoming protobuf buffer
* @param output a pointer to the BitswapBlock that will be allocated
* @returns true(1) on success, false(0) if not. If false, any memory was deallocated
*/
int ipfs_bitswap_message_block_protobuf_decode(uint8_t* buffer, size_t buffer_length, struct BitswapBlock** output);
/***
* Allocate memory for a new WantlistEntry
* @returns the newly allocated WantlistEntry
*/
struct WantlistEntry* ipfs_bitswap_wantlist_entry_new();
/***
* Free allocations of a WantlistEntry
* @param entry the WantlistEntry
* @returns true(1)
*/
int ipfs_bitswap_wantlist_entry_free(struct WantlistEntry* entry);
/**
* Retrieve an estimate of the size of a protobuf'd WantlistEntry
* @param entry the struct to examine
* @returns the approximate (maximum actually) size of a protobuf'd WantlistEntry
*/
size_t ipfs_bitswap_wantlist_entry_protobuf_encode_size(struct WantlistEntry* entry);
/***
* Encode a WantlistEntry into a Protobuf
* @param entry the WantlistEntry to encode
* @param buffer where to put the results
* @param buffer_length the maximum size of the buffer
* @param bytes_written the number of bytes written into the buffer
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_bitswap_wantlist_entry_protobuf_encode(struct WantlistEntry* entry, unsigned char* buffer, size_t buffer_length, size_t* bytes_written);
/***
* Decode a protobuf into a struct WantlistEntry
* @param buffer the protobuf buffer
* @param buffer_length the length of the data in the protobuf buffer
* @param output the resultant WantlistEntry
* @returns true(1) on success, otherwise false(0)
*/
int ipfs_bitswap_wantlist_entry_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct WantlistEntry** output);
/***
* Allocate memory for a new Bitswap Message WantList
* @returns the allocated struct BitswapWantlist
*/
struct BitswapWantlist* ipfs_bitswap_wantlist_new();
/**
* Free the resources used by a Wantlist
* @param list the list to free
* @returns true(1)
*/
int ipfs_bitswap_wantlist_free(struct BitswapWantlist* list);
/***
* Calculate the maximum size of a protobuf'd BitswapWantlist
* @param list the Wantlist
* @returns the maximum size of the protobuf'd list
*/
size_t ipfs_bitswap_wantlist_protobuf_encode_size(struct BitswapWantlist* list);
/***
* Encode a BitswapWantlist into a protobuf buffer
* @param list the list to encode
* @param buffer the buffer to fill
* @param buffer_length the length of the allocated buffer
* @param bytes_written the total number of bytes written to the buffer
* @returns true(1) on success, otherwise false(0)
*/
int ipfs_bitswap_wantlist_protobuf_encode(struct BitswapWantlist* list, unsigned char* buffer, size_t buffer_length, size_t* bytes_written);
/***
* Decode a Wantlist from a protobuf
* @param buffer the protobuf
* @param buffer_length the length of the protobuf
* @param output the newly allocated BitswapWantlist
* @returns true(1) on success, otherwise false(0)
*/
int ipfs_bitswap_wantlist_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct BitswapWantlist** output);
/***
* Bitswap Message
*
*/
/***
* Allocate memory for a new Bitswap Message
* @returns the allocated struct BitswapMessage
*/
struct BitswapMessage* ipfs_bitswap_message_new();
/**
* Free the resources used by a BitswapMessage
* @param message the BitswapMessage to free
* @returns true(1)
*/
int ipfs_bitswap_message_free(struct BitswapMessage* message);
/***
* Calculate the maximum size of a protobuf'd BitswapMessage
* @param message the BitswapMessage
* @returns the maximum size of the protobuf'd BitswapMessage
*/
size_t ipfs_bitswap_message_protobuf_encode_size(const struct BitswapMessage* message);
/***
* Encode a BitswapMessage into a protobuf buffer
* @param message the message to encode
* @param buffer the buffer to fill
* @param buffer_length the length of the allocated buffer
* @param bytes_written the total number of bytes written to the buffer
* @returns true(1) on success, otherwise false(0)
*/
int ipfs_bitswap_message_protobuf_encode(const struct BitswapMessage* message, unsigned char* buffer, size_t buffer_length, size_t* bytes_written);
/***
* Decode a BitswapMessage from a protobuf
* @param buffer the protobuf
* @param buffer_length the length of the protobuf
* @param output the newly allocated BitswapMessage
* @returns true(1) on success, otherwise false(0)
*/
int ipfs_bitswap_message_protobuf_decode(const uint8_t* buffer, size_t buffer_length, struct BitswapMessage** output);
/****
* Add a vector of Cids to the bitswap message
* @param message the message
* @param cids a Libp2pVector of cids
* @returns true(1) on success, otherwise false(0)
*/
int ipfs_bitswap_message_add_wantlist_items(struct BitswapMessage* message, struct Libp2pVector* cids);
/***
* Add the blocks to the BitswapMessage
* @param message the message
* @param blocks the requested blocks
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_bitswap_message_add_blocks(struct BitswapMessage* message, struct Libp2pVector* blocks, struct Libp2pVector* cids_they_want);

View file

@ -1,83 +0,0 @@
/***
* This implements the BitswapNetwork. Members of this network can fill requests and
* smartly handle queues of local and remote requests.
*/
#include "libp2p/conn/session.h"
#include "libp2p/peer/peer.h"
#include "ipfs/exchange/bitswap/bitswap.h"
#include "ipfs/exchange/bitswap/message.h"
struct BitswapRouting {
/**
* Find the provider of a key asyncronously
* @param context the session context
* @param hash the key we're looking for
* @param forWhat I have yet to research this
* @param responseMethod a function pointer to call when results are found
* @returns true(1) on success, otherwise false(0)
*/
int (*FindProviderAsync)(struct SessionContext* context, unsigned char* hash, int forWhat, void (*responseMethod)(void*));
/**
* Provides the key to the network. Is this an announcement or a fill?
* I think it is an announcement
* @param context the session context
* @param hash the hash to announce
* @returns true(1) on success, false(0) on error
*/
int (*Provide)(struct SessionContext* context, unsigned char* hash);
};
struct BitswapNetwork {
/***
* Send a message to a particular peer
* @param context the context
* @param peerId the peer ID of who to send to
* @param message the message to send
* @returns true(1) on success, false(0) otherwise
*/
int (*SendMessage)(struct SessionContext* context, unsigned char* peerId, struct BitswapMessage* message);
/**
* The BitswapReceiver is who receives messages from the network
* @param receiver the struct that contains function pointers for receiving messages
* @returns true(1) on success, otherwise false(0)
*/
//TODO: Implement this
//int (*SetDelegate)(struct BitswapReceiver* receiver);
/**
* Attempt a connection to a particular peer
* @param context the session context
* @param peerId the id of the peer
* @returns true(1) on success, otherwise false(0)
*/
int (*ConnectTo)(struct SessionContext* context, unsigned char* peerId);
/**
* A pointer to the method that creates a new BitswapMessageSender
* @param context the session context
* @param peerId the peer id of whom we should send the message to.
* @reutrns a pointer to the allocated struct that contains the initialized BitswapMessageSender or NULL if there was a problem
*/
struct BitswapMessageSender* (*NewMessageSender)(struct SessionContext* context, unsigned char* peerId);
};
/****
* send a message to a particular peer
* @param context the BitswapContext
* @param peer the peer that is the recipient
* @param message the message to send
*/
int ipfs_bitswap_network_send_message(const struct BitswapContext* context, struct Libp2pPeer* peer, const struct BitswapMessage* message);
/***
* Handle a raw incoming bitswap message from the network
* @param node us
* @param sessionContext the connection context
* @param bytes the message
* @param bytes_size the size of the message
* @returns true(1) on success, false(0) otherwise.
*/
int ipfs_bitswap_network_handle_message(const struct IpfsNode* node, const struct SessionContext* sessionContext, const uint8_t* bytes, size_t bytes_length);

View file

@ -1,145 +0,0 @@
#pragma once
/***
* A queue for requests to/from remote peers
* NOTE: This must handle multiple threads
*/
#include <pthread.h>
#include "libp2p/peer/peer.h"
#include "ipfs/exchange/bitswap/bitswap.h"
#include "ipfs/blocks/block.h"
struct CidEntry {
struct Cid* cid;
int cancel;
int cancel_has_been_sent;
int request_has_been_sent;
};
struct PeerRequest {
pthread_mutex_t request_mutex;
struct Libp2pPeer* peer;
// CidEntry collection of cids that they want
struct Libp2pVector* cids_they_want;
// CidEntry collection of cids that we want or are canceling
struct Libp2pVector* cids_we_want;
// blocks to send to them
struct Libp2pVector* blocks_we_want_to_send;
// blocks they sent us are processed immediately, so no queue necessary
// although the cid can go in cids_we_want again, with a cancel flag
};
struct PeerRequestEntry {
struct PeerRequestEntry* prior;
struct PeerRequest* current;
struct PeerRequestEntry* next;
};
struct PeerRequestQueue {
pthread_mutex_t queue_mutex;
struct PeerRequestEntry* first;
struct PeerRequestEntry* last;
};
/***
* Allocate memory for CidEntry
* @returns new CidEntry struct
*/
struct CidEntry* ipfs_bitswap_peer_request_cid_entry_new();
/**
* Allocate resources for a new PeerRequest
* @returns a new PeerRequest struct or NULL if there was a problem
*/
struct PeerRequest* ipfs_bitswap_peer_request_new();
/**
* Free resources from a PeerRequest
* @param request the request to free
* @returns true(1)
*/
int ipfs_bitswap_peer_request_free(struct PeerRequest* request);
/**
* Allocate resources for a new queue
* @returns a new PeerRequestQueue
*/
struct PeerRequestQueue* ipfs_bitswap_peer_request_queue_new();
/**
* Free all resources related to the queue
* @param queue the queue
* @returns true(1)
*/
int ipfs_bitswap_peer_request_queue_free(struct PeerRequestQueue* queue);
/**
* Adds a peer request to the end of the queue
* @param queue the queue
* @param request the request
* @returns true(1) on success, otherwise false
*/
int ipfs_bitswap_peer_request_queue_add(struct PeerRequestQueue* queue, struct PeerRequest* request);
/**
* Removes a peer request from the queue, no mather where it is
* @param queue the queue
* @param request the request
* @returns true(1) on success, otherwise false(0)
*/
int ipfs_bitswap_peer_request_quque_remove(struct PeerRequestQueue* queue, struct PeerRequest* request);
/**
* Pull a PeerRequest off the queue
* @param queue the queue
* @returns the PeerRequest that should be handled next.
*/
struct PeerRequest* ipfs_bitswap_peer_request_queue_pop(struct PeerRequestQueue* queue);
/**
* Finds a PeerRequestEntry that contains the specified PeerRequest
* @param queue the queue
* @param request what we're looking for
* @returns the PeerRequestEntry or NULL if not found
*/
struct PeerRequestEntry* ipfs_bitswap_peer_request_queue_find_entry(struct PeerRequestQueue* queue, struct Libp2pPeer* peer);
/***
* Add a block to the appropriate peer's queue
* @param queue the queue
* @param who the session context that identifies the peer
* @param block the block
* @returns true(1) on success, otherwise false(0)
*/
int ipfs_bitswap_peer_request_queue_fill(struct PeerRequestQueue* queue, struct Libp2pPeer* who, struct Block* block);
/***
* Allocate resources for a PeerRequestEntry struct
* @returns the allocated struct or NULL if there was a problem
*/
struct PeerRequestEntry* ipfs_bitswap_peer_request_entry_new();
/**
* Frees resources allocated
* @param entry the PeerRequestEntry to free
* @returns true(1)
*/
int ipfs_bitswap_peer_request_entry_free(struct PeerRequestEntry* entry);
/****
* Handle a PeerRequest
* @param context the BitswapContext
* @param request the request to process
* @returns true(1) on succes, otherwise false(0)
*/
int ipfs_bitswap_peer_request_process_entry(const struct BitswapContext* context, struct PeerRequest* request);
/***
* Find a PeerRequest related to a peer. If one is not found, it is created.
*
* @param peer_request_queue the queue to look through
* @param peer the peer to look for
* @returns a PeerRequestEntry or NULL on error
*/
struct PeerRequest* ipfs_peer_request_queue_find_peer(struct PeerRequestQueue* queue, struct Libp2pPeer* peer);

View file

@ -1,40 +0,0 @@
#pragma once
#include "ipfs/blocks/block.h"
#include "ipfs/cid/cid.h"
#include "ipfs/exchange/bitswap/bitswap.h"
#include "wantlist_queue.h"
/***
* Add a Cid to the local wantlist
* @param context the context
* @param cid the Cid
* @returns the added WantListQueueEntry
*/
struct WantListQueueEntry* ipfs_bitswap_want_manager_add(const struct BitswapContext* context, const struct Cid* cid, const struct WantListSession* session);
/***
* Checks to see if the requested block has been received
* @param context the context
* @param cid the Cid
* @returns true(1) if it has been received, false(0) otherwise
*/
int ipfs_bitswap_want_manager_received(const struct BitswapContext* context, const struct Cid* cid);
/***
* retrieve a block from the WantManager.
* @param context the context
* @param cid the Cid to get
* @param block a pointer to the block that will be allocated
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_bitswap_want_manager_get_block(const struct BitswapContext* context, const struct Cid* cid, struct Block** block);
/***
* We no longer are requesting this block, so remove it from the queue
* NOTE: This is reference counted, as another process may have asked for it.
* @param context the context
* @param cid the Cid
* @returns true(1) on success, false(0) otherwise.
*/
int ipfs_bitswap_want_manager_remove(const struct BitswapContext* context, const struct Cid* cid);

View file

@ -1,115 +0,0 @@
#pragma once
/**
* This is a list of requests from a peer (including locally).
* NOTE: This tracks who wants what. If 2 peers want the same file,
* there will be 1 WantListEntry in the WantList. There will be 2 entries in
* WantListEntry.sessionsRequesting.
*/
#include <pthread.h>
#include "ipfs/cid/cid.h"
#include "ipfs/blocks/block.h"
#include "ipfs/exchange/bitswap/bitswap.h"
enum WantListSessionType { WANTLIST_SESSION_TYPE_LOCAL, WANTLIST_SESSION_TYPE_REMOTE };
struct WantListSession {
enum WantListSessionType type;
void* context; // either an IpfsNode (local) or a Libp2pPeer (remote)
};
struct WantListQueueEntry {
struct Cid* cid;
int priority;
// a vector of WantListSessions
struct Libp2pVector* sessionsRequesting;
struct Block* block;
int asked_network;
int attempts;
};
struct WantListQueue {
pthread_mutex_t wantlist_mutex;
// a vector of WantListEntries
struct Libp2pVector* queue;
};
/***
* Initialize a WantListQueueEntry
* @returns a new WantListQueueEntry
*/
struct WantListQueueEntry* ipfs_bitswap_wantlist_queue_entry_new();
/***
* Remove resources, freeing a WantListQueueEntry
* @param entry the WantListQueueEntry
* @returns true(1)
*/
int ipfs_bitswap_wantlist_queue_entry_free(struct WantListQueueEntry* entry);
/***
* Initialize a new Wantlist (there should only be 1 per instance)
* @returns a new WantList
*/
struct WantListQueue* ipfs_bitswap_wantlist_queue_new();
/***
* Deallocate resources of a WantList
* @param wantlist the WantList
* @returns true(1)
*/
int ipfs_bitswap_wantlist_queue_free(struct WantListQueue* wantlist);
/***
* Add a Cid to the WantList
* @param wantlist the WantList to add to
* @param cid the Cid to add
* @returns the correct WantListEntry or NULL if error
*/
struct WantListQueueEntry* ipfs_bitswap_wantlist_queue_add(struct WantListQueue* wantlist, const struct Cid* cid, const struct WantListSession* session);
/***
* Remove (decrement the counter) a Cid from the WantList
* @param wantlist the WantList
* @param cid the Cid
* @returns true(1) on success, otherwise false(0)
*/
int ipfs_bitswap_wantlist_queue_remove(struct WantListQueue* wantlist, const struct Cid* cid, const struct WantListSession* session);
/***
* Find a Cid in the WantList
* @param wantlist the list
* @param cid the Cid
* @returns the WantListQueueEntry
*/
struct WantListQueueEntry* ipfs_bitswap_wantlist_queue_find(struct WantListQueue* wantlist, const struct Cid* cid);
/***
* compare 2 sessions for equality
* @param a side a
* @param b side b
* @returns 0 if equal, <0 if A wins, >0 if b wins
*/
int ipfs_bitswap_wantlist_session_compare(const struct WantListSession* a, const struct WantListSession* b);
/**
* Create a new WantListSession
* @returns the newly allocated WantListSession
*/
struct WantListSession* ipfs_bitswap_wantlist_session_new();
/**
* Called by the Bitswap engine, this processes an item on the WantListQueue
* @param context the context
* @param entry the WantListQueueEntry
* @returns true(1) on success, false(0) if not.
*/
int ipfs_bitswap_wantlist_process_entry(struct BitswapContext* context, struct WantListQueueEntry* entry);
/***
* Pops the top one off the queue
*
* @param wantlist the list
* @returns the WantListQueueEntry
*/
struct WantListQueueEntry* ipfs_bitswap_wantlist_queue_pop(struct WantListQueue* wantlist);

View file

@ -1,77 +0,0 @@
#pragma once
/**
* This is the definition of an "Exchange"
*
* Anything that implements the Exchange interface can be used as
* an IPFS block exchange protocol.
*/
#include "ipfs/blocks/block.h"
#include "ipfs/cid/cid.h"
#include "libp2p/utils/vector.h"
/**
* These are methods that the local IPFS daemon (or client)
* call to communicate with the local repository or network
*/
struct Exchange {
/**
* Retrieve a block from peers within the deadline enforced
* by the context
*
* NOTE: Shouldn't the block parameter be a callback (function pointer)?
* Otherwise, this function is going to block. Is that what we want?
*
* @param context the context
* @param cid the hash of the block to retrieve
* @param block a pointer to the block (allocated by this method if return is true)
* @returns true(1) on success, false(0) otherwise
*/
int (*GetBlock)(struct Exchange* exchange, struct Cid* cid, struct Block** block);
/**
* Retrieve a block from peers asynchronously
*
* @param context the context
* @param cid the hash of the block to retrieve
* @param queue_entry the queue entry to watch
* @returns true(1) on success, false(0) otherwise
*/
int (*GetBlockAsync)(struct Exchange* exchange, struct Cid* cid, struct Block** block);
/**
* Retrieve several blocks
* @param context the context
* @param Cids a vector of hashes for the blocks to be retrieved
* @param blocks a pointer to a vector of retrieved blocks (will be NULL on error)
* @returns true(1) on success, otherwise false(0)
*/
int (*GetBlocks)(struct Exchange* exchange, struct Libp2pVector* Cids, struct Libp2pVector** blocks);
/**
* Announces the existance of a block to this bitswap service. The service will
* potentially notify its peers.
* NOTE: This is mainly designed to announce blocks added by non-bitswap methods (i.e. the local user)
* @param block the block being announced
* @returns true(1) on success, false(0) if not
*/
int (*HasBlock)(struct Exchange* exchange, struct Block* block);
/**
* Determine if we're online
* @returns true(1) if we're online
*/
int (*IsOnline)(struct Exchange*);
/**
* Close up the exchange, and go offline
* @returns true(1);
*/
int (*Close)(struct Exchange*);
/**
* Used by each implementation to maintain state
* (will be cast-ed to an implementation-specific structure)
*/
void* exchangeContext;
};

View file

@ -1,56 +0,0 @@
#pragma once
#include "ipfs/cmd/cli.h"
#include "ipfs/core/ipfs_node.h"
/**
* Pull bytes from the hashtable
*/
/**
* get a file by its hash, and write the data to a file
* @param hash the base58 multihash of the cid
* @param file_name the file name to write to
* @returns true(1) on success
*/
int ipfs_exporter_to_file(const unsigned char* hash, const char* file_name, struct IpfsNode* local_node);
/***
* Retrieve a protobuf'd Node from the router
* @param local_node the context
* @param hash the hash to retrieve
* @param hash_size the length of the hash
* @param result a place to store the Node
* @returns true(1) on success, otherwise false(0)
*/
int ipfs_exporter_get_node(struct IpfsNode* local_node, const unsigned char* hash, const size_t hash_size, struct HashtableNode** result);
int ipfs_exporter_object_get(int argc, char** argv);
/***
* Called from the command line with ipfs cat [hash]. Retrieves the object pointed to by hash, and displays its block data (links and data elements)
* @param argc number of arguments
* @param argv arguments
* @param output_file where to stream the results
* @returns true(1) on success
*/
int ipfs_exporter_object_cat(struct CliArguments* args, FILE* output_file);
/**
* Retrieves the object pointed to by hash and displays the raw data
* @param local_node the local node
* @param hash the hash to use
* @param hash_size the length of the hash
* @param file the file descrptor to write to
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_exporter_object_cat_to_file(struct IpfsNode *local_node, unsigned char* hash, int hash_size, FILE* file);
/**
* rebuild a file based on this HashtableNode, traversing links
* @param node the HashtableNode to start with
* @param local_node the context
* @param file the filestream to fill
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_exporter_cat_node(struct HashtableNode* node, struct IpfsNode* local_node, FILE *file);

View file

@ -1,32 +0,0 @@
#ifndef __IPFS_IMPORTER_IMPORTER_H__
#define __IPFS_IMPORTER_IMPORTER_H__
#include "ipfs/cmd/cli.h"
#include "ipfs/merkledag/node.h"
#include "ipfs/core/ipfs_node.h"
/**
* Creates a node based on an incoming file or directory
* NOTE: this can be called recursively for directories
* NOTE: When this function completes, parent_node will be either:
* 1) the complete file, in the case of a small file (<256k-ish)
* 2) a node with links to the various pieces of a large file
* 3) a node with links to files and directories if 'fileName' is a directory
* @param root_dir the directory for where to look for the file
* @param file_name the file (or directory) to import
* @param parent_node the root node (has links to others in case this is a large file and is split)
* @param fs_repo the ipfs repository
* @param bytes_written number of bytes written to disk
* @param recursive true if we should navigate directories
* @returns true(1) on success
*/
int ipfs_import_file(const char* root, const char* fileName, struct HashtableNode** parent_node, struct IpfsNode *local_node, size_t* bytes_written, int recursive);
/**
* called from the command line
* @param argc the number of arguments
* @param argv the arguments
*/
int ipfs_import_files(struct CliArguments* args);
#endif /* INCLUDE_IPFS_IMPORTER_IMPORTER_H_ */

View file

@ -1,25 +0,0 @@
#pragma once
#include "ipfs/merkledag/node.h"
#include "ipfs/core/ipfs_node.h"
/**
* Implements a resover. EOM
*/
/**
* Interogate the path and the current node, looking
* for the desired node.
* @param path the current path
* @param from the current node (or NULL if it is the first call)
* @returns what we are looking for, or NULL if it wasn't found
*/
struct HashtableNode* ipfs_resolver_get(const char* path, struct HashtableNode* from, const struct IpfsNode* ipfs_node);
/**
* Interrogate the path, looking for the peer
* @param path the peer path to search for
* @param ipfs_node the context
* @returns the MultiAddress that relates to the path, or NULL if not found
*/
struct Libp2pPeer* ipfs_resolver_find_peer(const char* path, const struct IpfsNode* ipfs_node);

View file

@ -1,50 +0,0 @@
#pragma once
#include <stdio.h>
#include <stdint.h>
#include "libp2p/conn/session.h"
#include "ipfs/core/ipfs_node.h"
#include "libp2p/net/protocol.h"
/**
* The journal protocol attempts to keep a journal in sync with other (approved) nodes
*/
/***
* See if we can handle this message
* @param incoming the incoming message
* @param incoming_size the size of the incoming message
* @returns true(1) if the protocol in incoming is something we can handle. False(0) otherwise.
*/
int ipfs_journal_can_handle(const struct StreamMessage* msg);
/**
* Clean up resources used by this handler
* @param context the context to clean up
* @returns true(1)
*/
int ipfs_journal_shutdown_handler(void* context);
/***
* Handles a message
* @param incoming the message
* @param incoming_size the size of the message
* @param session_context details of the remote peer
* @param protocol_context in this case, an IpfsNode
* @returns 0 if the caller should not continue looping, <0 on error, >0 on success
*/
int ipfs_journal_handle_message(const struct StreamMessage* msg, struct Stream* stream, void* protocol_context) ;
/***
* Build the protocol handler struct for the Journal protocol
* @param local_node what to stuff in the context
* @returns the protocol handler
*/
struct Libp2pProtocolHandler* ipfs_journal_build_protocol_handler(const struct IpfsNode* local_node);
/***
* Send a journal message to a remote peer
* @param replication_peer the peer to send it to
* @returns true(1) on success, false(0) otherwise.
*/
int ipfs_journal_sync(struct IpfsNode* local_node, struct ReplicationPeer* replication_peer);

View file

@ -1,43 +0,0 @@
#pragma once
/**
* A journal entry protobuf
*/
#include <stdlib.h>
#include <stdint.h>
struct JournalEntry {
unsigned long long timestamp;
uint8_t pin;
uint8_t *hash;
size_t hash_size;
};
struct JournalEntry* ipfs_journal_entry_new();
int ipfs_journal_entry_free(struct JournalEntry* entry);
/**
* Determine the maximum size of a protobuf'd JournalEntry
*/
int ipfs_journal_entry_encode_size(struct JournalEntry* entry);
/***
* Protobuf the journal entry
* @param entry the JournalEntry to protobuf
* @param buffer where to place the results
* @param max_buffer_size the amount of memory allocated for the buffer
* @param bytes_used the amount of the buffer used
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_journal_entry_encode(struct JournalEntry* entry, uint8_t *buffer, size_t max_buffer_size, size_t *bytes_used);
/***
* Turn a protobuf'd JournalEntry and turn it into a real JournalEntry
* @param incoming the incoming bytes
* @param incoming_size the size of the incoming buffer
* @param results where to put the new JournalEntry
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_journal_entry_decode(uint8_t *incoming, size_t incoming_size, struct JournalEntry **results);

View file

@ -1,42 +0,0 @@
#pragma once
#include <stdlib.h>
#include <stdint.h>
#include "libp2p/utils/vector.h"
struct JournalMessage {
unsigned long long current_epoch;
unsigned long long start_epoch;
unsigned long long end_epoch;
struct Libp2pVector* journal_entries;
};
struct JournalMessage* ipfs_journal_message_new();
int ipfs_journal_message_free(struct JournalMessage* message);
/**
* Determine the maximum size of a protobuf'd JournalMessage
* @param message the JournalMessage
* @returns the maximum size of this message in bytes if it were protobuf'd
*/
int ipfs_journal_message_encode_size(struct JournalMessage* message);
/***
* Protobuf the journal message
* @param message the JournalMessage to protobuf
* @param buffer where to place the results
* @param max_buffer_size the amount of memory allocated for the buffer
* @param bytes_used the amount of the buffer used
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_journal_message_encode(struct JournalMessage* entry, uint8_t *buffer, size_t max_buffer_size, size_t *bytes_used);
/***
* Turn a protobuf'd JournalMessage and turn it into a real JournalMessage
* @param incoming the incoming bytes
* @param incoming_size the size of the incoming buffer
* @param results where to put the new JournalMessage
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_journal_message_decode(const uint8_t *incoming, size_t incoming_size, struct JournalMessage **results);

View file

@ -1,45 +0,0 @@
/***
* Merkledag methods
*/
#ifndef __IPFS_MERKLEDAG_H__
#define __IPFS_MERKLEDAG_H__
#include "ipfs/merkledag/node.h"
#include "ipfs/repo/fsrepo/fs_repo.h"
/***
* Adds a node to the dagService and blockService
* @param node the node to add
* @param fs_repo the repo to add to
* @param bytes_written the number of bytes written
* @returns true(1) on success
*/
int ipfs_merkledag_add(struct HashtableNode* node, struct FSRepo* fs_repo, size_t* bytes_written);
/***
* Retrieves a node from the datastore based on the cid
* @param cid the key to look for
* @param node the node to be created
* @param fs_repo the repository
* @returns true(1) on success
*/
int ipfs_merkledag_get(const unsigned char* hash, size_t hash_size, struct HashtableNode** node, const struct FSRepo* fs_repo);
/***
* Retrieves a node from the datastore based on the multihash
* @param multihash the base58 encoded multihash (should start with Qm) as a null terminated string
* @param node the node to be created
* @param fs_repo the repository
* @returns true(1) on success
*/
int ipfs_merkledag_get_by_multihash(const unsigned char* multihash, size_t multihash_length, struct HashtableNode** node, const struct FSRepo* fs_repo);
/***
* Convert the data within a block to a HashtableNode
* @param block the block
* @param node_ptr where to put the results
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_merkledag_convert_block_to_node(struct Block* block, struct HashtableNode** node_ptr);
#endif

View file

@ -1,296 +0,0 @@
/**
* An implementation of an IPFS node
* Copying the go-ipfs-node project
*/
#ifndef IPFS_NODE_H
#define IPFS_NODE_H
#include "ipfs/cid/cid.h"
/*====================================================================================
*
* Structures
*
*===================================================================================*/
struct NodeLink
{
size_t hash_size;
unsigned char* hash;
char* name;
size_t t_size;
struct NodeLink* next;
};
struct HashtableNode
{
// saved in protobuf
size_t data_size;
unsigned char* data;
struct NodeLink* head_link;
// not saved in protobuf
unsigned char* encoded;
// a base32 representation of the multihash
unsigned char* hash;
size_t hash_size;
};
/*====================================================================================
*
* Functions
*
*===================================================================================*/
/*====================================================================================
* Link Functions
*===================================================================================*/
/* Create_Link
* @Param name: The name of the link (char *)
* @Param ahash: An Qmhash
* @param hash_size the size of the hash
* @param node_link a pointer to the new struct NodeLink
* @returns true(1) on success
*/
int ipfs_node_link_create(char * name, unsigned char * ahash, size_t hash_size, struct NodeLink** node_link);
/****
* Allocate memory for a new NodeLink
* @param node_link a pointer to the newly allocated memory
* @returns true(1) on success
*/
int ipfs_node_link_new(struct NodeLink** node_link);
/* ipfs_node_link_free
* @param L: Free the link you have allocated.
*/
int ipfs_node_link_free(struct NodeLink * node_link);
/***
* Node protobuf functions
*/
/***
* Get the approximate size needed to protobuf encode this link
* @param link the link to examine
* @returns the maximum size that should be needed
*/
size_t ipfs_node_link_protobuf_encode_size(const struct NodeLink* link);
/***
* Encode a NodeLink into protobuf format
* @param link the link
* @param buffer where to put the encoded results
* @param max_buffer_length the max size that should be put in buffer
* @pram bytes_written the amount of the buffer used
* @returns true(1) on success
*/
int ipfs_node_link_protobuf_encode(const struct NodeLink* link, unsigned char* buffer, size_t max_buffer_length, size_t* bytes_written);
/****
* Decode from a byte array into a NodeLink
* @param buffer the byte array
* @param buffer_length the length of the byte array
* @param link the pointer to the new NodeLink (NOTE: Will be allocated in this function)
* @param bytes_read the amount of bytes read by this function
* @returns true(1) on success
*/
int ipfs_node_link_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct NodeLink** link);
/***
* return an approximate size of the encoded node
* @param node the node to examine
* @returns the max size of an encoded stream of bytes, if it were encoded
*/
size_t ipfs_hashtable_node_protobuf_encode_size(const struct HashtableNode* node);
/***
* Encode a node into a protobuf byte stream
* @param node the node to encode
* @param buffer where to put it
* @param max_buffer_length the length of buffer
* @param bytes_written how much of buffer was used
* @returns true(1) on success
*/
int ipfs_hashtable_node_protobuf_encode(const struct HashtableNode* node, unsigned char* buffer, size_t max_buffer_length, size_t* bytes_written);
/***
* Decode a stream of bytes into a Node structure
* @param buffer where to get the bytes from
* @param buffer_length the length of buffer
* @param node pointer to the Node to be created
* @returns true(1) on success
*/
int ipfs_hashtable_node_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct HashtableNode** node);
/*====================================================================================
* Node Functions
*===================================================================================*/
/****
* Creates an empty node, allocates the required memory
* @param node the pointer to the memory allocated
* @returns true(1) on success, otherwise false(0)
*/
int ipfs_hashtable_node_new(struct HashtableNode** node);
/***
* Allocates memory for a node, and sets the data section to indicate
* that this node is a directory
* @param node the node to initialize
* @returns true(1) on success, otherwise false(0)
*/
int ipfs_hashtable_node_create_directory(struct HashtableNode** node);
/***
* Determine if this node is actually a directory
* @param node the node to examine
* @returns true(1) if this node is a directory. Otherwise, false(0)
*/
int ipfs_hashtable_node_is_directory(struct HashtableNode* node);
/**
* sets the Cid into the struct element titled cached
* @param node the node to work with
* @param cid the cid
* @returns true(1) on success
*/
int ipfs_hashtable_node_set_hash(struct HashtableNode* node, const unsigned char* hash, size_t hash_size);
/*ipfs_node_set_data
* Sets the data of a node
* @param Node: The node which you want to set data in.
* @param Data, the data you want to assign to the node
* Sets pointers of encoded & cached to NULL /following go method
* returns 1 on success 0 on failure
*/
int ipfs_hashtable_node_set_data(struct HashtableNode * N, unsigned char * Data, size_t data_size);
/*ipfs_node_set_encoded
* @param NODE: the node you wish to alter (struct Node *)
* @param Data: The data you wish to set in encoded.(unsigned char *)
* returns 1 on success 0 on failure
*/
int ipfs_hashtable_node_set_encoded(struct HashtableNode * N, unsigned char * Data);
/*ipfs_node_get_data
* Gets data from a node
* @param Node: = The node you want to get data from. (unsigned char *)
* Returns data of node.
*/
unsigned char * ipfs_hashtable_node_get_data(struct HashtableNode * N);
/*ipfs_node_free
* Once you are finished using a node, always delete it using this.
* It will take care of the links inside it.
* @param N: the node you want to free. (struct Node *)
*/
int ipfs_hashtable_node_free(struct HashtableNode * N);
/*ipfs_node_get_link_by_name
* Returns a copy of the link with given name
* @param Name: (char * name) searches for link with this name
* Returns the link struct if it's found otherwise returns NULL
*/
struct NodeLink * ipfs_hashtable_node_get_link_by_name(struct HashtableNode * N, char * Name);
/*ipfs_node_remove_link_by_name
* Removes a link from node if found by name.
* @param name: Name of link (char * name)
* returns 1 on success, 0 on failure.
*/
int ipfs_hashtable_node_remove_link_by_name(char * Name, struct HashtableNode * mynode);
/* ipfs_node_add_link
* Adds a link to your node
* @param mynode: &yournode
* @param mylink: the CID you want to create a node from
* @param linksz: sizeof(your cid here)
* Returns your node with the newly added link
*/
int ipfs_hashtable_node_add_link(struct HashtableNode * mynode, struct NodeLink * mylink);
/*ipfs_node_new_from_link
* Create a node from a link
* @param mylink: the link you want to create it from. (struct Cid *)
* @param node the pointer to the new node
* @returns true(1) on success
*/
int ipfs_hashtable_node_new_from_link(struct NodeLink * mylink, struct HashtableNode** node);
/*ipfs_node_new_from_data
* @param data: bytes buffer you want to create the node from
* @param data_size the size of the data
* @param node the pointer to the new node
* @returns true(1) on success
*/
int ipfs_hashtable_node_new_from_data(unsigned char * data, size_t data_size, struct HashtableNode** node);
/***
* create a Node struct from encoded data
* @param data: encoded bytes buffer you want to create the node from. Note: this copies the pointer, not a memcpy
* @param node a pointer to the node that will be created
* @returns true(1) on success
*/
int ipfs_hashtable_node_new_from_encoded(unsigned char * data, struct HashtableNode** node);
/*Node_Resolve_Max_Size
* !!!This shouldn't concern you!
*Gets the ammount of words that will be returned by Node_Resolve
*@Param1: The string that will be processed (eg: char * sentence = "foo/bar/bin")
*Returns either -1 if something went wrong or the ammount of words that would be processed.
*/
int Node_Resolve_Max_Size(char * input1);
/*Node_Resolve Basically stores everything in a pointer array eg: char * bla[Max_Words_]
* !!!This shouldn't concern you!!!
*@param1: Pointer array(char * foo[x], X=Whatever ammount there is. should be used with the helper function Node_Resolve_Max_Size)
*@param2: Sentence to gather words/paths from (Eg: char * meh = "foo/bar/bin")
*@Returns 1 or 0, 0 if something went wrong, 1 if everything went smoothly.
*/
int Node_Resolve(char ** result, char * input1);
/**************************************************************************************************************************************
*|||||||||||||||||||||||||||||||||||||||| !!!! IMPORTANT !!! ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||*
**************************************************************************************************************************************
* Not sure where this is used, I'm making something to easen it up for all of you.
* This in itself will get all the links for you in a link[] array inside Link_Proc
* the memory allocation for storage will be noted in the ammount of links.
* After being done with this, you have to free the array for which you will have a function specially made for you.
*
* What this does:
* It searches for links using a path like /foo/bar/bin/, if links with those names are found in the node you specify, it stores them
* in a custom struct, Link_Proc; which is what Node_Resolve_Link returns.
* Notes:
* Use it, free it, it's all already laid out for you.
* There will also be a tutorial demonstrating it in the same folder here so everyone can understand this.
*/
struct Link_Proc
{
char * remaining_links; // Not your concern.
int ammount; //This will store the ammount of links, so you know what to process.
struct NodeLink * links[]; // Link array
};
/*Node_Resolve_Links
* Processes a path returning all links.
* @param N: The node you want to get links from
* @param path: The "foo/bar/bin" path
*/
struct Link_Proc * Node_Resolve_Links(struct HashtableNode * N, char * path);
/*Free_link_Proc
* frees the Link_Proc struct you created.
* @param1: Link_Proc struct (struct Link_Proc *)
*/
void Free_Link_Proc(struct Link_Proc * LPRC);
/*Node_Tree() Basically a unix-like ls
*@Param1: Result char * foo[strlen(sentence)]
*@Param2: char sentence[] = foo/bar/bin/whatever
*Return: 0 if failure, 1 if success
*/
int Node_Tree(char * result, char * input1); //I don't know where you use this but here it is.
#endif

View file

@ -1,21 +0,0 @@
#pragma once
#include "ipfs/cmd/cli.h"
/***
* Publish IPNS name
*/
int ipfs_name_publish(struct IpfsNode* local_node, char* name, char **response, size_t *response_size);
/***
* Resolve IPNS name
*/
int ipfs_name_resolve(struct IpfsNode* local_node, char* name, char **response, size_t *response_size);
/**
* We received a cli command "ipfs name". Do the right thing.
* @param argc number of arguments on the command line
* @param argv actual command line arguments
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_name(struct CliArguments* args);

View file

@ -3,7 +3,40 @@
#define DefaultDepthLimit 32
#include "ipfs/util/errs.h"
#ifdef NAMESYS_C
char *ErrNamesys[] = {
NULL,
"ErrAllocFailed",
"ErrNULLPointer",
"ErrPipe",
"ErrPoll",
"Could not publish name."
"Could not resolve name.",
"Could not resolve name (recursion limit exceeded).",
"expired record",
"unrecognized validity type",
"not a valid proquint string",
"not a valid domain name",
"not a valid dnslink entry"
};
#else
extern char *ErrNamesys;
#endif // NAMESYS_C
enum {
ErrAllocFailed = 1,
ErrNULLPointer,
ErrPipe,
ErrPoll,
ErrPublishFailed,
ErrResolveFailed,
ErrResolveRecursion,
ErrExpiredRecord,
ErrUnrecognizedValidity,
ErrInvalidProquint,
ErrInvalidDomain,
ErrInvalidDNSLink
} NamesysErrs;
typedef struct s_resolvers {
char *protocol;
@ -32,7 +65,7 @@
typedef struct s_mpns {
resolvers *resolver;
publishers *publisher;
publishers *Publisher;
} mpns;
typedef struct s_tlds {
@ -40,30 +73,30 @@
int condition;
} tlds;
int ipfs_namesys_resolve(char **path, char *name);
int ipfs_namesys_resolve_n(char **path, char *name, int depth);
int ipfs_namesys_resolve_once (char **path, char *name);
int ipfs_namesys_publish (char *proto, ciPrivKey name, char *value);
int ipfs_namesys_publish_with_eol (char *proto, ciPrivKey name, char *value, time_t eol);
int resolve (resolver *r, char **p, char *str, int depth, char **prefixes);
int Resolve(char **path, char *name);
int ResolveN(char **path, char *name, int depth);
int resolveOnce (char **path, char *name);
int Publish (char *proto, ciPrivKey name, char *value);
int PublishWithEOL (char *proto, ciPrivKey name, char *value, time_t eol);
int ipfs_proquint_is_proquint(char *str);
char *ipfs_proquint_encode(char *buf, int size);
char *ipfs_proquint_decode(char *str);
int ipfs_proquint_resolve_once (char **p, char *name);
int ProquintIsProquint(char *str);
char *ProquintEncode(char *buf, int size);
char *ProquintDecode(char *str);
int ProquintResolveOnce (char **p, char *name);
int ipfs_isdomain_match_string (char *d);
int ipfs_isdomain_is_icann_tld(char *s);
int ipfs_isdomain_is_extended_tld (char *s);
int ipfs_isdomain_is_tld (char *s);
int ipfs_isdomain_is_domain (char *s);
int domainMatchString (char *d);
int IsICANNTLD(char *s);
int IsExtendedTLD (char *s);
int IsTLD (char *s);
int IsDomain (char *s);
typedef struct s_DNSResolver {
// TODO
int (*lookupTXT) (char ***, char *);
} DNSResolver;
int ipfs_dns_resolver_resolve_once (char **path, char *name);
int ipfs_dns_work_domain (int output, DNSResolver *r, char *name);
int ipfs_dns_parse_entry (char **path, char *txt);
int ipfs_dns_try_parse_dns_link(char **path, char *txt);
int DNSResolverResolveOnce (DNSResolver *r, char **path, char *name);
int workDomain (int output, DNSResolver *r, char *name);
int parseEntry (char **Path, char *txt);
int tryParseDnsLink (char **Path, char *txt);
#endif //NAMESYS_H

View file

@ -1,38 +0,0 @@
#pragma once
#include <stdint.h>
typedef int32_t IpnsEntry_ValidityType;
struct ipns_entry {
char *value;
char *signature;
int32_t *validityType;
char *validity;
uint64_t *sequence;
uint64_t *ttl;
struct routingResolver *cache; // cache and eol should be the last items.
struct timespec *eol;
};
struct namesys_pb {
// TODO
struct ipns_entry *IpnsEntry;
};
// setting an EOL says "this record is valid until..."
const static IpnsEntry_ValidityType IpnsEntry_EOL = 0;
/*
static char *IpnsEntry_ValidityType_name[] = {
"EOL",
NULL
};
*/
int IpnsEntry_ValidityType_value (char *s);
struct ipns_entry* ipfs_namesys_pb_new_ipns_entry ();
char* ipfs_namesys_pb_get_validity (struct ipns_entry*);
char* ipns_entry_data_for_sig(struct ipns_entry*);
char* ipfs_ipns_entry_get_signature(struct ipns_entry*);
int ipfs_namesys_pb_get_value (char**, struct ipns_entry*);
IpnsEntry_ValidityType ipfs_namesys_pb_get_validity_type (struct ipns_entry*);
void ipfs_namesys_ipnsentry_reset (struct ipns_entry *m);

View file

@ -1,21 +0,0 @@
#pragma once
#include "ipfs/cid/cid.h"
#include "ipfs/core/ipfs_node.h"
#include "ipfs/namesys/pb.h"
char* ipns_entry_data_for_sig (struct ipns_entry *entry);
int ipns_selector_func (int *idx, struct ipns_entry ***recs, char *k, char **vals);
int ipns_select_record (int *idx, struct ipns_entry **recs, char **vals);
// ipns_validate_ipns_record implements ValidatorFunc and verifies that the
// given 'val' is an IpnsEntry and that that entry is valid.
int ipns_validate_ipns_record (char *k, char *val);
/**
* Store the hash locally, and notify the network
*
* @param local_node the context
* @param path the "/ipfs/" or "/ipns" path
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_namesys_publisher_publish(struct IpfsNode* local_node, char* path);

View file

@ -1,14 +0,0 @@
#pragma once
#include "ipfs/core/ipfs_node.h"
/**
* Resolve an IPNS name.
* NOTE: if recursive is set to false, the result could be another ipns path
* @param local_node the context
* @param path the ipns path (i.e. "/ipns/Qm12345..")
* @param recursive true to resolve until the result is not ipns, false to simply get the next step in the path
* @param result the results (i.e. "/ipfs/QmAb12CD...")
* @returns true(1) on success, false(0) otherwise
*/
int ipfs_namesys_resolver_resolve(struct IpfsNode* local_node, const char* path, int recursive, char** results);

View file

@ -1,40 +0,0 @@
#ifndef IPNS_NAMESYS_ROUTING_H
#define IPNS_NAMESYS_ROUTING_H
#include "mh/multihash.h"
#include "ipfs/util/time.h"
#include "ipfs/namesys/pb.h"
#define DefaultResolverCacheTTL 60 // a minute
struct cacheEntry {
char *key;
char *value;
struct timespec eol;
};
struct routingResolver {
int cachesize;
int next;
struct cacheEntry **data;
};
struct libp2p_routing_value_store { // dummy declaration, not implemented yet.
void *missing;
};
char* ipfs_routing_cache_get (char *key, struct ipns_entry *ientry);
void ipfs_routing_cache_set (char *key, char *value, struct ipns_entry *ientry);
struct routingResolver* ipfs_namesys_new_routing_resolver (struct libp2p_routing_value_store *route, int cachesize);
// ipfs_namesys_routing_resolve implements Resolver.
int ipfs_namesys_routing_resolve (char **path, char *name, struct namesys_pb *pb);
// ipfs_namesys_routing_resolve_n implements Resolver.
int ipfs_namesys_routing_resolve_n (char **path, char *name, int depth, struct namesys_pb *pb);
// ipfs_namesys_routing_resolve_once implements resolver. Uses the IPFS
// routing system to resolve SFS-like names.
int ipfs_namesys_routing_resolve_once (char **path, char *name, int depth, char *prefix, struct namesys_pb *pb);
int ipfs_namesys_routing_check_EOL (struct timespec *ts, struct namesys_pb *pb);
int ipfs_namesys_routing_get_value (char*, char*);
int ipfs_namesys_routing_getpublic_key (char*, unsigned char* multihash, size_t multihash_size);
#endif // IPNS_NAMESYS_ROUTING_H

View file

@ -43,15 +43,15 @@ int main(int argc, char** argv)
////Nodes/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//N_Create_From_Link
struct HashtableNode * Mynode;
struct Node * Mynode;
Mynode = N_Create_From_Link(mylink);
mylink->name = "HAHA";//Testing for valid node creation
printf("Node Link[0] Name: %s\nHash: %s\n",Mynode->head_link[0]->name, Mynode->head_link[0]->Lcid->hash);
printf("Node Link[0] Name: %s\nHash: %s\n",Mynode->links[0]->name, Mynode->links[0]->Lcid->hash);
//N_Add_Link
Mynode = N_Add_Link(&Mynode, mylink2, sizeof(mylink2));
mylink2->name = "HAHA";//Testing for valid node creation
printf("Node Link[1] Name: %s\nHash: %s\n",Mynode->head_link[1]->name,Mynode->head_link[1]->Lcid->hash);
printf("Node Link[1] Name: %s\nHash: %s\n",Mynode->links[1]->name,Mynode->links[1]->Lcid->hash);
//Node_Get_Link
struct Link * ResultLink = Node_Get_Link(Mynode, "Simo");
@ -72,9 +72,9 @@ int main(int argc, char** argv)
printf("Outlinkamt: %d\n", Mynode->link_ammount);
//Node Copy
struct HashtableNode * Node2;
struct Node * Node2;
Node2 = Node_Copy(Mynode);
printf("NODE COPY TEST: [0]: %s\n", Node2->head_link[0]->Lcid->hash);
printf("NODE COPY TEST: [0]: %s\n", Node2->links[0]->Lcid->hash);
Node_Delete(Node2);
//Node_Set_Data

405
include/ipfs/node/node.h Normal file
View file

@ -0,0 +1,405 @@
/**
* An implementation of an IPFS node
* Copying the go-ipfs-node project
*/
#ifndef IPFS_NODE_H
#define IPFS_NODE_H
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "inttypes.h"
#include "ipfs/cid/cid.h"
int debug = 0; // Set to 1 to enable debug mode of this program
/*====================================================================================
*
* Structures
*
*===================================================================================*/
struct Link
{
char * name;
size_t size;
struct Cid * Lcid;
};
struct Node
{
unsigned char * data;
unsigned char * encoded;
struct Cid * cached;
int link_ammount;
struct Link * links[];
};
/*====================================================================================
*
* Functions
*
*===================================================================================*/
/*====================================================================================
* Link Functions
*===================================================================================*/
/* Create_Link
* @Param name: The name of the link (char *)
* @Param size: Size of the link (size_t)
* @Param ahash: An Qmhash
*/
struct Link * Create_Link(char * name, unsigned char * ahash)
{
struct Link * mylink;
mylink = malloc(sizeof(struct Link));
mylink->name = name;
int ver = 0;
size_t lenhash = strlen(ahash)-1;
ipfs_cid_new(ver, ahash, lenhash*2, CID_PROTOBUF, &mylink->Lcid);
mylink->size = sizeof(mylink) + mylink->Lcid->hash_length; //Unsure of this
return mylink;
}
/* Free_Link
* @param L: Free the link you have allocated.
*/
void Free_Link(struct Link * L)
{
ipfs_cid_free(L->Lcid);
free(L);
}
/*====================================================================================
* Node Functions
*===================================================================================*/
/*Create_Empty_Node
* Creates an empty node, allocates the required memory
* Returns a fresh new node with no data set in it.
*/
struct Node * Create_Empty_Node()
{
struct Node * N;
N = (struct Node *)malloc(sizeof(struct Node));
return N;
}
/*Node_Set_Data
* Sets the data of a node
* @param Node: The node which you want to set data in.
* @param Data, the data you want to assign to the node
* returns 1 on success 0 on failure
*/
int Node_Set_Data(struct Node * N, unsigned char * Data)
{
if(!N || !Data)
{
return 0;
}
N->encoded = NULL;
N->cached = NULL;
N->data = Data;
return 1;
}
/*Node_Get_Data
* Gets data from a node
* @param Node: = The node you want to get data from. (unsigned char *)
* Returns data of node.
*/
unsigned char * Node_Get_Data(struct Node * N)
{
unsigned char * DATA;
DATA = N->data;
return DATA;
}
/*Node_Copy: Returns a copy of the node you input
* @param Node: The node you want to copy (struct CP_Node *)
* Returns a copy of the node you wanted to copy.
*/
struct Node * Node_Copy(struct Node * CP_Node)
{
struct Node * CN;
CN = (struct Node*) malloc(sizeof(struct Node) + sizeof(struct Link) * 2);
if(CP_Node->link_ammount != 0)
{
for(int i=0; i<CP_Node->link_ammount; i++)
{
CN->links[i] = malloc(sizeof(struct Link));
}
}
memcpy(CN, CP_Node, sizeof(struct Node));
memcpy(CN->links[0],CP_Node->links[0], sizeof(struct Link));
return CN;
}
/*Node_Delete
* Once you are finished using a node, always delete it using this.
* It will take care of the links inside it.
* @param N: the node you want to free. (struct Node *)
*/
void Node_Delete(struct Node * N)
{
if(N->link_ammount > 0)
{
for(int i=0; i<N->link_ammount; i++)
{
free(N->links[i]);
}
}
free(N);
}
/*Node_Get_Link
* Returns a copy of the link with given name
* @param Name: (char * name) searches for link with this name
* Returns the link struct if it's found otherwise returns NULL
*/
struct Link * Node_Get_Link(struct Node * N, char * Name)
{
struct Link * L;
for(int i=0;i<N->link_ammount;i++)
{
if(strcmp(N->links[i]->name,Name) == 0)
{
L = (struct Link *)malloc(sizeof(struct Link));
memcpy(L,N->links[i],sizeof(struct Link));
int ver = L->Lcid->version;
char * ahash = L->Lcid->hash;
size_t lenhash = L->Lcid->hash_length;
ipfs_cid_new(ver, ahash, lenhash, CID_PROTOBUF, &L->Lcid);
return L;
}
}
return NULL;
}
/*Node_Remove_Link
* Removes a link from node if found by name.
* @param name: Name of link (char * name)
* returns 1 on success, 0 on failure.
*/
int Node_Remove_Link(char * Name, struct Node * mynode)
{
for(int i=0; i<mynode->link_ammount; i++)
{
if(mynode->links[i]->name == Name)
{
for(int x=i;x<mynode->link_ammount && x+1 != mynode->link_ammount;i++)
{
memcpy(mynode->links[x],mynode->links[x+1],sizeof(struct Link));
}
free(mynode->links[mynode->link_ammount-1]);
mynode->link_ammount--;
return 1;
}
}
return 0;
}
/* N_Add_Link
* Adds a link to your node
* @param mynode: &yournode
* @param mylink: the CID you want to create a node from
* @param linksz: sizeof(your cid here)
* Returns your node with the newly added link
*/
struct Node * N_Add_Link(struct Node ** mynode, struct Link * mylink, size_t linksz)
{
struct Node * Nl = *mynode;
Nl->link_ammount++;
size_t calculatesize = 0;
if(Nl->link_ammount != 0)
{
for(int i=0; i<Nl->link_ammount-1;i++)
{
calculatesize = calculatesize + sizeof(Nl->links[i]);
}
calculatesize = calculatesize + linksz;
Nl = (struct Node *) realloc(Nl, sizeof(struct Node) + calculatesize);
}
else
{
Nl = (struct Node *) malloc(sizeof(struct Node) + linksz);
}
Nl->links[Nl->link_ammount-1] = malloc(sizeof(struct Link));
memcpy(Nl->links[Nl->link_ammount-1],mylink,sizeof(struct Link));
return Nl;
}
/*N_Create_From_Link
* Create a node from a link
* @param mylink: the link you want to create it from. (struct Cid *)
* @param linksize: sizeof(the link in mylink) (size_T)
* Returns a fresh new node with the link you specified. Has to be freed with Node_Free preferably.
*/
struct Node * N_Create_From_Link(struct Link * mylink)
{
struct Node * mynode;
mynode = (struct Node *) malloc(sizeof(struct Node) + sizeof(struct Link));
mynode->link_ammount = 0;
mynode->link_ammount++;
mynode->links[0] = malloc(sizeof(struct Link));
memcpy(mynode->links[0], mylink, sizeof(struct Link));
return mynode;
}
/*N_Create_From_Data
* @param data: bytes buffer you want to create the node from
* returns a node with the data you inputted.
*/
struct Node * N_Create_From_Data(unsigned char * data)
{
struct Node * mynode;
mynode = (struct Node *) malloc(sizeof(struct Node));
mynode->data = data;
return mynode;
}
/*Node_Resolve_Max_Size
* !!!This shouldn't concern you!
*Gets the ammount of words that will be returned by Node_Resolve
*@Param1: The string that will be processed (eg: char * sentence = "foo/bar/bin")
*Returns either -1 if something went wrong or the ammount of words that would be processed.
*/
int Node_Resolve_Max_Size(char * input1)
{
if(!input1)
{
return -1; // Input is null, therefor nothing can be processed.
}
char input[strlen(input1)];
bzero(input, strlen(input1));
strcpy(input, input1);
int num = 0;
char * tr;
char * end;
tr=strtok_r(input,"/",&end);
for(int i = 0;tr;i++)
{
tr=strtok_r(NULL,"/",&end);
num++;
}
return num;
}
/*Node_Resolve Basically stores everything in a pointer array eg: char * bla[Max_Words_]
* !!!This shouldn't concern you!!!
*@param1: Pointer array(char * foo[x], X=Whatever ammount there is. should be used with the helper function Node_Resolve_Max_Size)
*@param2: Sentence to gather words/paths from (Eg: char * meh = "foo/bar/bin")
*@Returns 1 or 0, 0 if something went wrong, 1 if everything went smoothly.
*/
int Node_Resolve(char ** result, char * input1)
{
if(!input1)
{
return 0; // Input is null, therefor nothing can be processed.
}
char input[strlen(input1)];
bzero(input, strlen(input1));
strcpy(input, input1);
char * tr;
char * end;
tr=strtok_r(input,"/",&end);
for(int i = 0;tr;i++)
{
if(debug == 1)
{
printf("TR: %s\n", tr);
}
result[i] = (char *) malloc(strlen(tr)+1);
strcpy(result[i], tr);
tr=strtok_r(NULL,"/",&end);
}
return 1;
}
/**************************************************************************************************************************************
*|||||||||||||||||||||||||||||||||||||||| !!!! IMPORTANT !!! ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||*
**************************************************************************************************************************************
* Not sure where this is used, I'm making something to easen it up for all of you.
* This in itself will get all the links for you in a link[] array inside Link_Proc
* the memory allocation for storage will be noted in the ammount of links.
* After being done with this, you have to free the array for which you will have a function specially made for you.
*
* What this does:
* It searches for links using a path like /foo/bar/bin/, if links with those names are found in the node you specify, it stores them
* in a custom struct, Link_Proc; which is what Node_Resolve_Link returns.
* Notes:
* Use it, free it, it's all already laid out for you.
* There will also be a tutorial demonstrating it in the same folder here so everyone can understand this.
*/
struct Link_Proc
{
char * remaining_links; // Not your concern.
int ammount; //This will store the ammount of links, so you know what to process.
struct Link * links[]; // Link array
};
/*Node_Resolve_Links
* Processes a path returning all links.
* @param N: The node you want to get links from
* @param path: The "foo/bar/bin" path
*/
struct Link_Proc * Node_Resolve_Links(struct Node * N, char * path)
{
if(!N || !path)
{
return NULL;
}
int expected_link_ammount = Node_Resolve_Max_Size(path);
struct Link_Proc * LProc = (struct Link_Proc *) malloc(sizeof(struct Link_Proc) + sizeof(struct Link) * expected_link_ammount);
LProc->ammount = 0;
char * linknames[expected_link_ammount];
Node_Resolve(linknames, path);
for(int i=0;i<expected_link_ammount; i++)
{
struct Link * proclink;
proclink = Node_Get_Link(N, linknames[i]);
if(proclink)
{
LProc->links[i] = (struct Link *)malloc(sizeof(struct Link));
memcpy(LProc->links[i], proclink, sizeof(struct Link));
LProc->ammount++;
free(proclink);
}
}
//Freeing pointer array
for(int i=0;i<expected_link_ammount; i++)
{
free(linknames[i]);
}
return LProc;
}
/*Free_link_Proc
* frees the Link_Proc struct you created.
* @param1: Link_Proc struct (struct Link_Proc *)
*/
void Free_Link_Proc(struct Link_Proc * LPRC)
{
if(LPRC->ammount != 0)
{
for(int i=0;i<LPRC->ammount;i++)
{
Free_Link(LPRC->links[i]);
}
}
free(LPRC);
}
/*Node_Tree() Basically a unix-like ls
*@Param1: Result char * foo[strlen(sentence)]
*@Param2: char sentence[] = foo/bar/bin/whatever
*Return: 0 if failure, 1 if success
*/
int Node_Tree(char * result, char * input1) //I don't know where you use this but here it is.
{
if(!input1)
{
return 0;
}
char input[strlen(input1)];
bzero(input, strlen(input1));
strcpy(input, input1);
for(int i=0; i<strlen(input); i++)
{
if(input[i] == '/')
{
input[i] = ' ';
}
}
strcpy(result, input);
if(debug == 1)
{
printf("Node_Tree Internal Result: %s\n",result);
}
return 1;
}
#endif

Some files were not shown because too many files have changed in this diff Show more