libs3-2.0/0000755000175000001440000000000011640554415011057 5ustar bjiuserslibs3-2.0/test/0000755000175000001440000000000011640553711012034 5ustar bjiuserslibs3-2.0/test/goodxml_03.xml0000644000175000001440000004715011640553711014540 0ustar bjiusers 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 libs3-2.0/test/goodxml_01.xml0000644000175000001440000000042211640553711014525 0ustar bjiusers NoSuchKey The resource & then]]> you requested does not exist & so there /mybucket/myfoto.jpg 4442587FB7D0A2F9 libs3-2.0/test/badxml_01.xml0000644000175000001440000000256711640553711014337 0ustar bjiusers Data libs3-2.0/test/goodxml_02.xml0000644000175000001440000000256511640553711014540 0ustar bjiusers Data libs3-2.0/test/test.sh0000755000175000001440000001164211640553711013356 0ustar bjiusers#!/bin/sh # Environment: # S3_ACCESS_KEY_ID - must be set to S3 Access Key ID # S3_SECRET_ACCESS_KEY - must be set to S3 Secret Access Key # TEST_BUCKET_PREFIX - must be set to the test bucket prefix to use # S3_COMMAND - may be set to s3 command to use (i.e. valgrind s3); defaults # to "s3" if [ -z "$S3_ACCESS_KEY_ID" ]; then echo "S3_ACCESS_KEY_ID required" exit -1; fi if [ -z "$S3_SECRET_ACCESS_KEY" ]; then echo "S3_SECRET_ACCESS_KEY required" exit -1; fi if [ -z "$TEST_BUCKET_PREFIX" ]; then echo "TEST_BUCKET_PREFIX required" exit -1; fi if [ -z "$S3_COMMAND" ]; then S3_COMMAND=s3 fi TEST_BUCKET=${TEST_BUCKET_PREFIX}.testbucket # Create the test bucket in EU echo "$S3_COMMAND create $TEST_BUCKET locationConstraint=EU" $S3_COMMAND create $TEST_BUCKET # List to find it echo "$S3_COMMAND list | grep $TEST_BUCKET" $S3_COMMAND list | grep $TEST_BUCKET # Test it echo "$S3_COMMAND test $TEST_BUCKET" $S3_COMMAND test $TEST_BUCKET # List to ensure that it is empty echo "$S3_COMMAND list $TEST_BUCKET" $S3_COMMAND list $TEST_BUCKET # Put some data rm -f seqdata seq 1 10000 > seqdata echo "$S3_COMMAND put $TEST_BUCKET/testkey filename=seqdata noStatus=1" $S3_COMMAND put $TEST_BUCKET/testkey filename=seqdata noStatus=1 rm -f testkey # Get the data and make sure that it matches echo "$S3_COMMAND get $TEST_BUCKET/testkey filename=testkey" $S3_COMMAND get $TEST_BUCKET/testkey filename=testkey diff seqdata testkey rm -f seqdata testkey # Delete the file echo "$S3_COMMAND delete $TEST_BUCKET/testkey" $S3_COMMAND delete $TEST_BUCKET/testkey # Remove the test bucket echo "$S3_COMMAND delete $TEST_BUCKET" $S3_COMMAND delete $TEST_BUCKET # Make sure it's not there echo "$S3_COMMAND list | grep $TEST_BUCKET" $S3_COMMAND list | grep $TEST_BUCKET # Now create it again echo "$S3_COMMAND create $TEST_BUCKET" $S3_COMMAND create $TEST_BUCKET # Put 10 files in it for i in `seq 0 9`; do echo "echo \"Hello\" | $S3_COMMAND put $TEST_BUCKET/key_$i" echo "Hello" | $S3_COMMAND put $TEST_BUCKET/key_$i done # List with all details echo "$S3_COMMAND list $TEST_BUCKET allDetails=1" $S3_COMMAND list $TEST_BUCKET allDetails=1 COPY_BUCKET=${TEST_BUCKET_PREFIX}.copybucket # Create another test bucket and copy a file into it echo "$S3_COMMAND create $COPY_BUCKET" $S3_COMMAND create $COPY_BUCKET echo <> acl Group Authenticated AWS Users READ EOF echo <> acl Group All Users READ_ACP EOF echo "$S3_COMMAND setacl $TEST_BUCKET filename=acl" $S3_COMMAND setacl $TEST_BUCKET filename=acl # Test to make sure that it worked rm -f acl_new echo "$S3_COMMAND getacl $TEST_BUCKET filename=acl_new allDetails=1" $S3_COMMAND getacl $TEST_BUCKET filename=acl_new allDetails=1 diff acl acl_new rm -f acl acl_new # Get the key acl rm -f acl echo "$S3_COMMAND getacl $TEST_BUCKET/aclkey filename=acl allDetails=1" $S3_COMMAND getacl $TEST_BUCKET/aclkey filename=acl allDetails=1 # Add READ for all AWS users, and READ_ACP for everyone echo <> acl Group Authenticated AWS Users READ EOF echo <> acl Group All Users READ_ACP EOF echo "$S3_COMMAND setacl $TEST_BUCKET/aclkey filename=acl" $S3_COMMAND setacl $TEST_BUCKET/aclkey filename=acl # Test to make sure that it worked rm -f acl_new echo "$S3_COMMAND getacl $TEST_BUCKET/aclkey filename=acl_new allDetails=1" $S3_COMMAND getacl $TEST_BUCKET/aclkey filename=acl_new allDetails=1 diff acl acl_new rm -f acl acl_new # Remove the test file echo "$S3_COMMAND delete $TEST_BUCKET/aclkey" $S3_COMMAND delete $TEST_BUCKET/aclkey echo "$S3_COMMAND delete $TEST_BUCKET" $S3_COMMAND delete $TEST_BUCKET libs3-2.0/GNUmakefile.mingw0000644000175000001440000002344211640553711014254 0ustar bjiusers# GNUmakefile.mingw # # Copyright 2008 Bryan Ischo # # This file is part of libs3. # # libs3 is free software: you can redistribute it and/or modify it under the # terms of the GNU General Public License as published by the Free Software # Foundation, version 3 of the License. # # In addition, as a special exception, the copyright holders give # permission to link the code of this library and its programs with the # OpenSSL library, and distribute linked combinations including the two. # # libs3 is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License version 3 # along with libs3, in a file named COPYING. If not, see # . # I tried to use the autoconf/automake/autolocal/etc (i.e. autohell) tools # but I just couldn't stomach them. Since this is a Makefile for POSIX # systems, I will simply do away with autohell completely and use a GNU # Makefile. GNU make ought to be available pretty much everywhere, so I # don't see this being a significant issue for portability. # All commands assume a GNU compiler. For systems which do not use a GNU # compiler, write scripts with the same names as these commands, and taking # the same arguments, and translate the arguments and commands into the # appropriate non-POSIX ones as needed. libs3 assumes a GNU toolchain as # the most portable way to build software possible. Non-POSIX, non-GNU # systems can do the work of supporting this build infrastructure. # -------------------------------------------------------------------------- # Set libs3 version number, unless it is already set. # This is trunk0.trunk0 on the libs3 git master branch; release branches # are created with this set to specific version numbers when releases are # made. LIBS3_VER_MAJOR ?= 2 LIBS3_VER_MINOR ?= 0 LIBS3_VER := $(LIBS3_VER_MAJOR).$(LIBS3_VER_MINOR) # ----------------------------------------------------------------------------- # Determine verbosity. VERBOSE_SHOW should be prepended to every command which # should only be displayed if VERBOSE is set. QUIET_ECHO may be used to # echo text only if VERBOSE is not set. Typically, a VERBOSE_SHOW command will # be paired with a QUIET_ECHO command, to provide a command which is displayed # in VERBOSE mode, along with text which is displayed in non-VERBOSE mode to # describe the command. # # No matter what VERBOSE is defined to, it ends up as true if it's defined. # This will be weird if you defined VERBOSE=false in the environment, and we # switch it to true here; but the meaning of VERBOSE is, "if it's defined to # any value, then verbosity is turned on". So don't define VERBOSE if you # don't want verbosity in the build process. # ----------------------------------------------------------------------------- ifdef VERBOSE VERBOSE = true VERBOSE_ECHO = @ echo VERBOSE_SHOW = QUIET_ECHO = @ echo >nul else VERBOSE = false VERBOSE_ECHO = @ echo >nul VERBOSE_SHOW = @ QUIET_ECHO = @ echo endif # -------------------------------------------------------------------------- # BUILD directory ifndef BUILD ifdef DEBUG BUILD := build-debug else BUILD := build endif endif # -------------------------------------------------------------------------- # DESTDIR directory ifndef DESTDIR DESTDIR := libs3-$(LIBS3_VER) endif # -------------------------------------------------------------------------- # Acquire configuration information for libraries that libs3 depends upon ifndef CURL_LIBS CURL_LIBS := -Lc:\libs3-libs\bin -lcurl endif ifndef CURL_CFLAGS CURL_CFLAGS := -Ic:\libs3-libs\include endif ifndef LIBXML2_LIBS LIBXML2_LIBS := -Lc:\libs3-libs\bin -lxml2 endif ifndef LIBXML2_CFLAGS LIBXML2_CFLAGS := -Ic:\libs3-libs\include endif # -------------------------------------------------------------------------- # These CFLAGS assume a GNU compiler. For other compilers, write a script # which converts these arguments into their equivalent for that particular # compiler. ifndef CFLAGS ifdef DEBUG CFLAGS := -g else CFLAGS := -O3 endif endif CFLAGS += -Wall -Werror -Wshadow -Wextra -Iinc \ $(CURL_CFLAGS) $(LIBXML2_CFLAGS) \ -DLIBS3_VER_MAJOR=\"$(LIBS3_VER_MAJOR)\" \ -DLIBS3_VER_MINOR=\"$(LIBS3_VER_MINOR)\" \ -DLIBS3_VER=\"$(LIBS3_VER)\" \ -D__STRICT_ANSI__ \ -D_ISOC99_SOURCE \ -D_POSIX_C_SOURCE=200112L \ -Dsleep=Sleep -DSLEEP_UNITS_PER_SECOND=1000 \ -DFOPEN_EXTRA_FLAGS=\"b\" \ -Iinc/mingw -include windows.h LDFLAGS = $(CURL_LIBS) $(LIBXML2_LIBS) # -------------------------------------------------------------------------- # Default targets are everything .PHONY: all all: exported test # -------------------------------------------------------------------------- # Exported targets are the library and driver program .PHONY: exported exported: libs3 s3 headers # -------------------------------------------------------------------------- # Install target .PHONY: install install: exported $(QUIET_ECHO) $(DESTDIR)/bin/s3.exe: Installing executable - @ mkdir $(DESTDIR)\bin 2>&1 | echo >nul $(VERBOSE_SHOW) copy $(BUILD)\bin\s3.exe $(DESTDIR)\bin\s3.exe >nul $(QUIET_ECHO) $(DESTDIR)/bin/libs3/dll: Installing dynamic library $(VERBOSE_SHOW) copy $(BUILD)\bin\libs3.dll $(DESTDIR)\bin\libs3.dll >nul $(QUIET_ECHO) $(DESTDIR)/lib/libs3.a: Installing static library - @ mkdir $(DESTDIR)\lib 2>&1 | echo >nul $(VERBOSE_SHOW) copy $(BUILD)\lib\libs3.a $(DESTDIR)\lib\libs3.a >nul $(QUIET_ECHO) $(DESTDIR)/lib/libs3.def: Installing def file $(VERBOSE_SHOW) copy mswin\libs3.def $(DESTDIR)\lib\libs3.def >nul - @ mkdir $(DESTDIR)\include 2>&1 | echo >nul $(QUIET_ECHO) $(DESTDIR)/include/libs3.h: Copying header $(VERBOSE_SHOW) copy $(BUILD)\include\libs3.h \ $(DESTDIR)\include\libs3.h >nul $(QUIET_ECHO) $(DESTDIR)/LICENSE: Copying license $(VERBOSE_SHOW) copy LICENSE $(DESTDIR)\LICENSE >nul $(QUIET_ECHO) $(DESTDIR)/COPYING: Copying license $(VERBOSE_SHOW) copy COPYING $(DESTDIR)\COPYING >nul # -------------------------------------------------------------------------- # Uninstall target .PHONY: uninstall uninstall: $(QUIET_ECHO) Installed files: Uninstalling $(VERBOSE_SHOW) \ del $(DESTDIR)\bin\s3.exe \ $(DESTDIR)\bin\libs3.dll \ $(DESTDIR)\lib\libs3.a \ $(DESTDIR)\lib\libs3.def \ $(DESTDIR)\include\libs3.h \ $(DESTDIR)\LICENSE \ $(DESTDIR)\COPYING # -------------------------------------------------------------------------- # Compile target patterns $(BUILD)/obj/%.o: src/%.c $(QUIET_ECHO) $@: Compiling object - @ mkdir $(subst /,\,$(dir $(BUILD)/dep/$<)) 2>&1 | echo >nul @ gcc $(CFLAGS) -M -MG -MQ $@ -DCOMPILINGDEPENDENCIES \ -o $(BUILD)/dep/$(<:%.c=%.d) -c $< - @ mkdir $(subst /,\,$(dir $@)) 2>&1 | echo >nul $(VERBOSE_SHOW) gcc $(CFLAGS) -o $@ -c $< # -------------------------------------------------------------------------- # libs3 library targets LIBS3_SHARED = $(BUILD)/bin/libs3.dll LIBS3_STATIC = $(BUILD)/lib/libs3.a .PHONY: libs3 libs3: $(LIBS3_SHARED) $(BUILD)/lib/libs3.a LIBS3_SOURCES := src/acl.c src/bucket.c src/error_parser.c src/general.c \ src/object.c src/request.c src/request_context.c \ src/response_headers_handler.c src/service_access_logging.c \ src/service.c src/simplexml.c src/util.c src/mingw_functions.c $(LIBS3_SHARED): $(LIBS3_SOURCES:src/%.c=$(BUILD)/obj/%.o) $(QUIET_ECHO) $@: Building dynamic library - @ mkdir $(subst /,\,$(dir $@)) 2>&1 | echo >nul $(VERBOSE_SHOW) gcc -shared -o $@ $^ $(LDFLAGS) -lws2_32 $(LIBS3_STATIC): $(LIBS3_SHARED) $(QUIET_ECHO) $@: Building static library - @ mkdir $(subst /,\,$(dir $@)) 2>&1 | echo >nul $(VERBOSE_SHOW) dlltool --def mswin\libs3.def --dllname $(subst /,\,$<) \ --output-lib $(subst /,\,$@) # -------------------------------------------------------------------------- # Driver program targets .PHONY: s3 s3: $(BUILD)/bin/s3.exe $(BUILD)/bin/s3.exe: $(BUILD)/obj/s3.o $(BUILD)/obj/mingw_s3_functions.o \ $(BUILD)/lib/libs3.a $(QUIET_ECHO) $@: Building executable - @ mkdir $(subst /,\,$(dir $@)) 2>&1 | echo >nul $(VERBOSE_SHOW) gcc -o $@ $^ $(LDFLAGS) -lws2_32 # -------------------------------------------------------------------------- # libs3 header targets .PHONY: headers headers: $(BUILD)\include\libs3.h $(BUILD)\include\libs3.h: inc\libs3.h $(QUIET_ECHO) $@: Copying header - @ mkdir $(subst /,\,$(dir $@)) 2>&1 | echo >nul $(VERBOSE_SHOW) copy $< $@ # -------------------------------------------------------------------------- # Test targets .PHONY: test test: $(BUILD)/bin/testsimplexml $(BUILD)/bin/testsimplexml: $(BUILD)/obj/testsimplexml.o \ $(BUILD)/obj/simplexml.o $(QUIET_ECHO) $@: Building executable - @ mkdir $(subst /,\,$(dir $@)) 2>&1 | echo >nul $(VERBOSE_SHOW) gcc -o $@ $^ $(LIBXML2_LIBS) # -------------------------------------------------------------------------- # Clean target .PHONY: clean clean: $(QUIET_ECHO) $(BUILD): Cleaning $(VERBOSE_SHOW) mswin\rmrf.bat $(BUILD) # -------------------------------------------------------------------------- # Clean dependencies target .PHONY: cleandeps cleandeps: $(QUIET_ECHO) $(BUILD)/dep: Cleaning dependencies $(VERBOSE_SHOW) mswin\rmrf.bat $(BUILD)\dep # -------------------------------------------------------------------------- # Dependencies ALL_SOURCES := $(LIBS3_SOURCES) s3.c testsimplexml.c $(foreach i, $(ALL_SOURCES), $(eval -include $(BUILD)/dep/src/$(i:%.c=%.d))) libs3-2.0/GNUmakefile0000644000175000001440000003156111640553711013135 0ustar bjiusers# GNUmakefile # # Copyright 2008 Bryan Ischo # # This file is part of libs3. # # libs3 is free software: you can redistribute it and/or modify it under the # terms of the GNU General Public License as published by the Free Software # Foundation, version 3 of the License. # # In addition, as a special exception, the copyright holders give # permission to link the code of this library and its programs with the # OpenSSL library, and distribute linked combinations including the two. # # libs3 is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License version 3 # along with libs3, in a file named COPYING. If not, see # . # I tried to use the autoconf/automake/autolocal/etc (i.e. autohell) tools # but I just couldn't stomach them. Since this is a Makefile for POSIX # systems, I will simply do away with autohell completely and use a GNU # Makefile. GNU make ought to be available pretty much everywhere, so I # don't see this being a significant issue for portability. # All commands assume a GNU compiler. For systems which do not use a GNU # compiler, write scripts with the same names as these commands, and taking # the same arguments, and translate the arguments and commands into the # appropriate non-POSIX ones as needed. libs3 assumes a GNU toolchain as # the most portable way to build software possible. Non-POSIX, non-GNU # systems can do the work of supporting this build infrastructure. # -------------------------------------------------------------------------- # Set libs3 version number, unless it is already set. # This is trunk0.trunk0 on the libs3 git master branch; release branches # are created with this set to specific version numbers when releases are # made. LIBS3_VER_MAJOR ?= 2 LIBS3_VER_MINOR ?= 0 LIBS3_VER := $(LIBS3_VER_MAJOR).$(LIBS3_VER_MINOR) # ----------------------------------------------------------------------------- # Determine verbosity. VERBOSE_SHOW should be prepended to every command which # should only be displayed if VERBOSE is set. QUIET_ECHO may be used to # echo text only if VERBOSE is not set. Typically, a VERBOSE_SHOW command will # be paired with a QUIET_ECHO command, to provide a command which is displayed # in VERBOSE mode, along with text which is displayed in non-VERBOSE mode to # describe the command. # # No matter what VERBOSE is defined to, it ends up as true if it's defined. # This will be weird if you defined VERBOSE=false in the environment, and we # switch it to true here; but the meaning of VERBOSE is, "if it's defined to # any value, then verbosity is turned on". So don't define VERBOSE if you # don't want verbosity in the build process. # ----------------------------------------------------------------------------- ifdef VERBOSE VERBOSE = true VERBOSE_ECHO = @ echo VERBOSE_SHOW = QUIET_ECHO = @ echo > /dev/null else VERBOSE = false VERBOSE_ECHO = @ echo > /dev/null VERBOSE_SHOW = @ QUIET_ECHO = @ echo endif # -------------------------------------------------------------------------- # BUILD directory ifndef BUILD ifdef DEBUG BUILD := build-debug else BUILD := build endif endif # -------------------------------------------------------------------------- # DESTDIR directory ifndef DESTDIR DESTDIR := /usr endif # -------------------------------------------------------------------------- # Acquire configuration information for libraries that libs3 depends upon ifndef CURL_LIBS CURL_LIBS := $(shell curl-config --libs) endif ifndef CURL_CFLAGS CURL_CFLAGS := $(shell curl-config --cflags) endif ifndef LIBXML2_LIBS LIBXML2_LIBS := $(shell xml2-config --libs) endif ifndef LIBXML2_CFLAGS LIBXML2_CFLAGS := $(shell xml2-config --cflags) endif # -------------------------------------------------------------------------- # These CFLAGS assume a GNU compiler. For other compilers, write a script # which converts these arguments into their equivalent for that particular # compiler. ifndef CFLAGS ifdef DEBUG CFLAGS := -g else CFLAGS := -O3 endif endif CFLAGS += -Wall -Werror -Wshadow -Wextra -Iinc \ $(CURL_CFLAGS) $(LIBXML2_CFLAGS) \ -DLIBS3_VER_MAJOR=\"$(LIBS3_VER_MAJOR)\" \ -DLIBS3_VER_MINOR=\"$(LIBS3_VER_MINOR)\" \ -DLIBS3_VER=\"$(LIBS3_VER)\" \ -D__STRICT_ANSI__ \ -D_ISOC99_SOURCE \ -D_POSIX_C_SOURCE=200112L LDFLAGS = $(CURL_LIBS) $(LIBXML2_LIBS) -lpthread # -------------------------------------------------------------------------- # Default targets are everything .PHONY: all all: exported test # -------------------------------------------------------------------------- # Exported targets are the library and driver program .PHONY: exported exported: libs3 s3 headers # -------------------------------------------------------------------------- # Install target .PHONY: install install: exported $(QUIET_ECHO) $(DESTDIR)/bin/s3: Installing executable $(VERBOSE_SHOW) install -Dps -m u+rwx,go+rx $(BUILD)/bin/s3 \ $(DESTDIR)/bin/s3 $(QUIET_ECHO) \ $(DESTDIR)/lib/libs3.so.$(LIBS3_VER): Installing shared library $(VERBOSE_SHOW) install -Dps -m u+rw,go+r \ $(BUILD)/lib/libs3.so.$(LIBS3_VER_MAJOR) \ $(DESTDIR)/lib/libs3.so.$(LIBS3_VER) $(QUIET_ECHO) \ $(DESTDIR)/lib/libs3.so.$(LIBS3_VER_MAJOR): Linking shared library $(VERBOSE_SHOW) ln -sf libs3.so.$(LIBS3_VER) \ $(DESTDIR)/lib/libs3.so.$(LIBS3_VER_MAJOR) $(QUIET_ECHO) $(DESTDIR)/lib/libs3.so: Linking shared library $(VERBOSE_SHOW) ln -sf libs3.so.$(LIBS3_VER_MAJOR) $(DESTDIR)/lib/libs3.so $(QUIET_ECHO) $(DESTDIR)/lib/libs3.a: Installing static library $(VERBOSE_SHOW) install -Dp -m u+rw,go+r $(BUILD)/lib/libs3.a \ $(DESTDIR)/lib/libs3.a $(QUIET_ECHO) $(DESTDIR)/include/libs3.h: Installing header $(VERBOSE_SHOW) install -Dp -m u+rw,go+r $(BUILD)/include/libs3.h \ $(DESTDIR)/include/libs3.h # -------------------------------------------------------------------------- # Uninstall target .PHONY: uninstall uninstall: $(QUIET_ECHO) Installed files: Uninstalling $(VERBOSE_SHOW) \ rm -f $(DESTDIR)/bin/s3 \ $(DESTDIR)/include/libs3.h \ $(DESTDIR)/lib/libs3.a \ $(DESTDIR)/lib/libs3.so \ $(DESTDIR)/lib/libs3.so.$(LIBS3_VER_MAJOR) \ $(DESTDIR)/lib/libs3.so.$(LIBS3_VER) # -------------------------------------------------------------------------- # Compile target patterns $(BUILD)/obj/%.o: src/%.c $(QUIET_ECHO) $@: Compiling object @ mkdir -p $(dir $(BUILD)/dep/$<) @ gcc $(CFLAGS) -M -MG -MQ $@ -DCOMPILINGDEPENDENCIES \ -o $(BUILD)/dep/$(<:%.c=%.d) -c $< @ mkdir -p $(dir $@) $(VERBOSE_SHOW) gcc $(CFLAGS) -o $@ -c $< $(BUILD)/obj/%.do: src/%.c $(QUIET_ECHO) $@: Compiling dynamic object @ mkdir -p $(dir $(BUILD)/dep/$<) @ gcc $(CFLAGS) -M -MG -MQ $@ -DCOMPILINGDEPENDENCIES \ -o $(BUILD)/dep/$(<:%.c=%.dd) -c $< @ mkdir -p $(dir $@) $(VERBOSE_SHOW) gcc $(CFLAGS) -fpic -fPIC -o $@ -c $< # -------------------------------------------------------------------------- # libs3 library targets LIBS3_SHARED = $(BUILD)/lib/libs3.so.$(LIBS3_VER_MAJOR) LIBS3_STATIC = $(BUILD)/lib/libs3.a .PHONY: libs3 libs3: $(LIBS3_SHARED) $(LIBS3_STATIC) LIBS3_SOURCES := acl.c bucket.c error_parser.c general.c \ object.c request.c request_context.c \ response_headers_handler.c service_access_logging.c \ service.c simplexml.c util.c $(LIBS3_SHARED): $(LIBS3_SOURCES:%.c=$(BUILD)/obj/%.do) $(QUIET_ECHO) $@: Building shared library @ mkdir -p $(dir $@) $(VERBOSE_SHOW) gcc -shared -Wl,-soname,libs3.so.$(LIBS3_VER_MAJOR) \ -o $@ $^ $(LDFLAGS) $(LIBS3_STATIC): $(LIBS3_SOURCES:%.c=$(BUILD)/obj/%.o) $(QUIET_ECHO) $@: Building static library @ mkdir -p $(dir $@) $(VERBOSE_SHOW) $(AR) cr $@ $^ # -------------------------------------------------------------------------- # Driver program targets .PHONY: s3 s3: $(BUILD)/bin/s3 $(BUILD)/bin/s3: $(BUILD)/obj/s3.o $(LIBS3_SHARED) $(QUIET_ECHO) $@: Building executable @ mkdir -p $(dir $@) $(VERBOSE_SHOW) gcc -o $@ $^ $(LDFLAGS) # -------------------------------------------------------------------------- # libs3 header targets .PHONY: headers headers: $(BUILD)/include/libs3.h $(BUILD)/include/libs3.h: inc/libs3.h $(QUIET_ECHO) $@: Linking header @ mkdir -p $(dir $@) $(VERBOSE_SHOW) ln -sf $(abspath $<) $@ # -------------------------------------------------------------------------- # Test targets .PHONY: test test: $(BUILD)/bin/testsimplexml $(BUILD)/bin/testsimplexml: $(BUILD)/obj/testsimplexml.o $(LIBS3_STATIC) $(QUIET_ECHO) $@: Building executable @ mkdir -p $(dir $@) $(VERBOSE_SHOW) gcc -o $@ $^ $(LIBXML2_LIBS) # -------------------------------------------------------------------------- # Clean target .PHONY: clean clean: $(QUIET_ECHO) $(BUILD): Cleaning $(VERBOSE_SHOW) rm -rf $(BUILD) # -------------------------------------------------------------------------- # Clean dependencies target .PHONY: cleandeps cleandeps: $(QUIET_ECHO) $(BUILD)/dep: Cleaning dependencies $(VERBOSE_SHOW) rm -rf $(BUILD)/dep # -------------------------------------------------------------------------- # Dependencies ALL_SOURCES := $(LIBS3_SOURCES) s3.c testsimplexml.c $(foreach i, $(ALL_SOURCES), $(eval -include $(BUILD)/dep/src/$(i:%.c=%.d))) $(foreach i, $(ALL_SOURCES), $(eval -include $(BUILD)/dep/src/$(i:%.c=%.dd))) # -------------------------------------------------------------------------- # Debian package target DEBPKG = $(BUILD)/pkg/libs3_$(LIBS3_VER).deb DEBDEVPKG = $(BUILD)/pkg/libs3-dev_$(LIBS3_VER).deb .PHONY: deb deb: $(DEBPKG) $(DEBDEVPKG) $(DEBPKG): DEBARCH = $(shell dpkg-architecture | grep ^DEB_BUILD_ARCH= | \ cut -d '=' -f 2) $(DEBPKG): exported $(BUILD)/deb/DEBIAN/control $(BUILD)/deb/DEBIAN/shlibs \ $(BUILD)/deb/DEBIAN/postinst \ $(BUILD)/deb/usr/share/doc/libs3/changelog.gz \ $(BUILD)/deb/usr/share/doc/libs3/changelog.Debian.gz \ $(BUILD)/deb/usr/share/doc/libs3/copyright DESTDIR=$(BUILD)/deb/usr $(MAKE) install rm -rf $(BUILD)/deb/usr/include rm -f $(BUILD)/deb/usr/lib/libs3.a @mkdir -p $(dir $@) fakeroot dpkg-deb -b $(BUILD)/deb $@ mv $@ $(BUILD)/pkg/libs3_$(LIBS3_VER)_$(DEBARCH).deb $(DEBDEVPKG): DEBARCH = $(shell dpkg-architecture | grep ^DEB_BUILD_ARCH= | \ cut -d '=' -f 2) $(DEBDEVPKG): exported $(BUILD)/deb-dev/DEBIAN/control \ $(BUILD)/deb-dev/usr/share/doc/libs3-dev/changelog.gz \ $(BUILD)/deb-dev/usr/share/doc/libs3-dev/changelog.Debian.gz \ $(BUILD)/deb-dev/usr/share/doc/libs3-dev/copyright DESTDIR=$(BUILD)/deb-dev/usr $(MAKE) install rm -rf $(BUILD)/deb-dev/usr/bin rm -f $(BUILD)/deb-dev/usr/lib/libs3.so* @mkdir -p $(dir $@) fakeroot dpkg-deb -b $(BUILD)/deb-dev $@ mv $@ $(BUILD)/pkg/libs3-dev_$(LIBS3_VER)_$(DEBARCH).deb $(BUILD)/deb/DEBIAN/control: debian/control @mkdir -p $(dir $@) echo -n "Depends: " > $@ dpkg-shlibdeps -Sbuild -O $(BUILD)/lib/libs3.so.$(LIBS3_VER_MAJOR) | \ cut -d '=' -f 2- >> $@ sed -e 's/LIBS3_VERSION/$(LIBS3_VER)/' \ < $< | sed -e 's/DEBIAN_ARCHITECTURE/$(DEBARCH)/' | \ grep -v ^Source: >> $@ $(BUILD)/deb-dev/DEBIAN/control: debian/control.dev @mkdir -p $(dir $@) sed -e 's/LIBS3_VERSION/$(LIBS3_VER)/' \ < $< | sed -e 's/DEBIAN_ARCHITECTURE/$(DEBARCH)/' > $@ $(BUILD)/deb/DEBIAN/shlibs: echo -n "libs3 $(LIBS3_VER_MAJOR) libs3 " > $@ echo "(>= $(LIBS3_VER))" >> $@ $(BUILD)/deb/DEBIAN/postinst: debian/postinst @mkdir -p $(dir $@) cp $< $@ $(BUILD)/deb/usr/share/doc/libs3/copyright: LICENSE @mkdir -p $(dir $@) cp $< $@ @echo >> $@ @echo -n "An alternate location for the GNU General Public " >> $@ @echo "License version 3 on Debian" >> $@ @echo "systems is /usr/share/common-licenses/GPL-3." >> $@ $(BUILD)/deb-dev/usr/share/doc/libs3-dev/copyright: LICENSE @mkdir -p $(dir $@) cp $< $@ @echo >> $@ @echo -n "An alternate location for the GNU General Public " >> $@ @echo "License version 3 on Debian" >> $@ @echo "systems is /usr/share/common-licenses/GPL-3." >> $@ $(BUILD)/deb/usr/share/doc/libs3/changelog.gz: debian/changelog @mkdir -p $(dir $@) gzip --best -c $< > $@ $(BUILD)/deb-dev/usr/share/doc/libs3-dev/changelog.gz: debian/changelog @mkdir -p $(dir $@) gzip --best -c $< > $@ $(BUILD)/deb/usr/share/doc/libs3/changelog.Debian.gz: debian/changelog.Debian @mkdir -p $(dir $@) gzip --best -c $< > $@ $(BUILD)/deb-dev/usr/share/doc/libs3-dev/changelog.Debian.gz: \ debian/changelog.Debian @mkdir -p $(dir $@) gzip --best -c $< > $@ libs3-2.0/libs3.spec0000644000175000001440000000430111640553711012743 0ustar bjiusersSummary: C Library and Tools for Amazon S3 Access Name: libs3 Version: 2.0 Release: 1 License: GPL Group: Networking/Utilities URL: http://sourceforge.net/projects/reallibs3 Source0: libs3-2.0.tar.gz Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-root # Want to include curl dependencies, but older Fedora Core uses curl-devel, # and newer Fedora Core uses libcurl-devel ... have to figure out how to # handle this problem, but for now, just don't check for any curl libraries # Buildrequires: curl-devel Buildrequires: libxml2-devel Buildrequires: openssl-devel Buildrequires: make # Requires: libcurl Requires: libxml2 Requires: openssl %define debug_package %{nil} %description This package includes the libs3 shared object library, needed to run applications compiled against libs3, and additionally contains the s3 utility for accessing Amazon S3. %package devel Summary: Headers and documentation for libs3 Group: Development/Libraries Requires: %{name} = %{version}-%{release} %description devel This library provides an API for using Amazon's S3 service (see http://s3.amazonaws.com). Its design goals are: - To provide a simple and straightforward API for accessing all of S3's functionality - To not require the developer using libs3 to need to know anything about: - HTTP - XML - SSL In other words, this API is meant to stand on its own, without requiring any implicit knowledge of how S3 services are accessed using HTTP protocols. - To be usable from multithreaded code - To be usable by code which wants to process multiple S3 requests simultaneously from a single thread - To be usable in the simple, straightforward way using sequentialized blocking requests %prep %setup -q %build BUILD=$RPM_BUILD_ROOT/build make exported %install BUILD=$RPM_BUILD_ROOT/build DESTDIR=$RPM_BUILD_ROOT/usr make install rm -rf $RPM_BUILD_ROOT/build %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root,-) /usr/bin/s3 /usr/lib/libs3.so* %files devel %defattr(-,root,root,-) /usr/include/libs3.h /usr/lib/libs3.a %changelog * Sat Aug 09 2008 Bryan Ischo - Split into regular and devel packages. * Tue Aug 05 2008 Bryan Ischo - Initial build. libs3-2.0/README0000644000175000001440000000017611640553711011741 0ustar bjiusersThis directory contains the libs3 library. The libs3 library is free software. See the file LICENSE for copying permission. libs3-2.0/archlinux/0000755000175000001440000000000011640553711013052 5ustar bjiuserslibs3-2.0/archlinux/PKGBUILD0000644000175000001440000000114111640553711014173 0ustar bjiusers# Contributor: Bryan Ischo pkgname=libs3 pkgver=2.0 pkgrel=1 pkgdesc="C Library and Tools for Amazon S3 Access" arch=('i686' 'x86_64') url="http://libs3.ischo.com/index.html" license=('GPL') groups=() depends=('libxml2' 'openssl' 'curl') makedepends=('make' 'libxml2' 'openssl' 'curl') provides=() conflicts=() replaces=() backup=() options=() install= source=(http://libs3.ischo.com/$pkgname-$pkgver.tar.gz) noextract=() md5sums=('source md5') #generate with 'makepkg -g' build() { cd "$srcdir/$pkgname-$pkgver" DESTDIR=$pkgdir/usr make install || return 1 } # vim:set ts=2 sw=2 et: libs3-2.0/.gitignore0000644000175000001440000000002311640553711013040 0ustar bjiusersbuild build-debug libs3-2.0/INSTALL0000644000175000001440000000456011640553711012113 0ustar bjiusers To install libs3 on a POSIX system (except Microsoft Windows): -------------------------------------------------------------- Note that all POSIX builds have prerequisites, such as development libraries that libs3 requires and that must be installed at the time that libs3 is built. The easiest way to find out what those are, is to run the build command and then observe the results. *** For RPM-based systems (Fedora Core, Mandrake, etc) *** * rpmbuild -ta for example: rpmbuild -ta libs3-0.3.tar.gz *** For dpkg-based systems (Debian, Ubuntu, etc) *** * make deb This will produce a Debian package in the build/pkg directory. *** For all other systems *** * make [DESTDIR=destination root] install DESTDIR defaults to /usr To install libs3 on a Microsoft Windows system: ----------------------------------------------- *** Using MingW *** * libs3 can be built on Windows using the MingW compiler. No other tool is needed. However, the following libraries are needed to build libs3: - curl development libraries - libxml2 development libraries, and the libraries that it requires: - iconv - zlib These projects are independent of libs3, and their release schedule and means of distribution would make it very difficult to provide links to the files to download and keep them up-to-date in this file, so no attempt is made here. Development libraries and other files can be placed in: c:\libs3-libs\bin c:\libs3-libs\include If the above locations are used, then the GNUmakefile.mingw will work with no special caveats. If the above locations are not used, then the following environment variables should be set: CURL_LIBS should be set to the MingW compiler flags needed to locate and link in the curl libraries CURL_CFLAGS should be set to the MingW compiler flags needed to locate and include the curl headers LIBXML2_LIBS should be set to the MingW compiler flags needed to locate and link in the libxml2 libraries LIBXML2_CFLAGS should be set to the MingW compiler flags needed to locate and include the libxml2 headers * mingw32-make [DESTDIR=destination] -f GNUmakefile.mingw install DESTDIR defaults to libs3- * DESTDIR can be zipped up into a .zip file for distribution. For best results, the dependent libraries (curl, openssl, etc) should be included, along with their licenses. libs3-2.0/inc/0000755000175000001440000000000011640553711011626 5ustar bjiuserslibs3-2.0/inc/response_headers_handler.h0000644000175000001440000000442411640553711017031 0ustar bjiusers/** ************************************************************************** * response_headers_handler.h * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ #ifndef RESPONSE_HEADERS_HANDLER_H #define RESPONSE_HEADERS_HANDLER_H #include "libs3.h" #include "string_buffer.h" #include "util.h" typedef struct ResponseHeadersHandler { // The structure to pass to the headers callback. This is filled in by // the ResponseHeadersHandler from the headers added to it. S3ResponseProperties responseProperties; // Set to 1 after the done call has been made int done; // copied into here. We allow 128 bytes for each header, plus \0 term. string_multibuffer(responsePropertyStrings, 5 * 129); // responseproperties.metaHeaders strings get copied into here string_multibuffer(responseMetaDataStrings, COMPACTED_METADATA_BUFFER_SIZE); // Response meta data S3NameValue responseMetaData[S3_MAX_METADATA_COUNT]; } ResponseHeadersHandler; void response_headers_handler_initialize(ResponseHeadersHandler *handler); void response_headers_handler_add(ResponseHeadersHandler *handler, char *data, int dataLen); void response_headers_handler_done(ResponseHeadersHandler *handler, CURL *curl); #endif /* RESPONSE_HEADERS_HANDLER_H */ libs3-2.0/inc/request.h0000644000175000001440000001341311640553711013471 0ustar bjiusers/** ************************************************************************** * request.h * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ #ifndef REQUEST_H #define REQUEST_H #include "libs3.h" #include "error_parser.h" #include "response_headers_handler.h" #include "util.h" // Describes a type of HTTP request (these are our supported HTTP "verbs") typedef enum { HttpRequestTypeGET, HttpRequestTypeHEAD, HttpRequestTypePUT, HttpRequestTypeCOPY, HttpRequestTypeDELETE } HttpRequestType; // This completely describes a request. A RequestParams is not required to be // allocated from the heap and its lifetime is not assumed to extend beyond // the lifetime of the function to which it has been passed. typedef struct RequestParams { // Request type, affects the HTTP verb used HttpRequestType httpRequestType; // Bucket context for request S3BucketContext bucketContext; // Key, if any const char *key; // Query params - ready to append to URI (i.e. ?p1=v1?p2=v2) const char *queryParams; // sub resource, like ?acl, ?location, ?torrent, ?logging const char *subResource; // If this is a copy operation, this gives the source bucket const char *copySourceBucketName; // If this is a copy operation, this gives the source key const char *copySourceKey; // Get conditions const S3GetConditions *getConditions; // Start byte uint64_t startByte; // Byte count uint64_t byteCount; // Put properties const S3PutProperties *putProperties; // Callback to be made when headers are available. Might not be called. S3ResponsePropertiesCallback *propertiesCallback; // Callback to be made to supply data to send to S3. Might not be called. S3PutObjectDataCallback *toS3Callback; // Number of bytes total that readCallback will supply int64_t toS3CallbackTotalSize; // Callback to be made that supplies data read from S3. // Might not be called. S3GetObjectDataCallback *fromS3Callback; // Callback to be made when request is complete. This will *always* be // called. S3ResponseCompleteCallback *completeCallback; // Data passed to the callbacks void *callbackData; } RequestParams; // This is the stuff associated with a request that needs to be on the heap // (and thus live while a curl_multi is in use). typedef struct Request { // These put the request on a doubly-linked list of requests in a // request context, *if* the request is in a request context (else these // will both be 0) struct Request *prev, *next; // The status of this Request, as will be reported to the user via the // complete callback S3Status status; // The HTTP code returned by the S3 server, if it is known. Would rather // not have to keep track of this but S3 doesn't always indicate its // errors the same way int httpResponseCode; // The HTTP headers to use for the curl request struct curl_slist *headers; // The CURL structure driving the request CURL *curl; // libcurl requires that the uri be stored outside of the curl handle char uri[MAX_URI_SIZE + 1]; // Callback to be made when headers are available. Might not be called. S3ResponsePropertiesCallback *propertiesCallback; // Callback to be made to supply data to send to S3. Might not be called. S3PutObjectDataCallback *toS3Callback; // Number of bytes total that readCallback has left to supply int64_t toS3CallbackBytesRemaining; // Callback to be made that supplies data read from S3. // Might not be called. S3GetObjectDataCallback *fromS3Callback; // Callback to be made when request is complete. This will *always* be // called. S3ResponseCompleteCallback *completeCallback; // Data passed to the callbacks void *callbackData; // Handler of response headers ResponseHeadersHandler responseHeadersHandler; // This is set to nonzero after the properties callback has been made int propertiesCallbackMade; // Parser of errors ErrorParser errorParser; } Request; // Request functions // ---------------------------------------------------------------------------- // Initialize the API S3Status request_api_initialize(const char *userAgentInfo, int flags, const char *hostName); // Deinitialize the API void request_api_deinitialize(); // Perform a request; if context is 0, performs the request immediately; // otherwise, sets it up to be performed by context. void request_perform(const RequestParams *params, S3RequestContext *context); // Called by the internal request code or internal request context code when a // curl has finished the request void request_finish(Request *request); // Convert a CURLE code to an S3Status S3Status request_curl_code_to_status(CURLcode code); #endif /* REQUEST_H */ libs3-2.0/inc/libs3.h0000644000175000001440000024077711640553711013034 0ustar bjiusers/** ************************************************************************** * libs3.h * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ #ifndef LIBS3_H #define LIBS3_H #include #include #ifdef __cplusplus extern "C" { #endif /** ************************************************************************** * Overview * -------- * * This library provides an API for using Amazon's S3 service (see * http://s3.amazonaws.com). Its design goals are: * * - To provide a simple and straightforward API for accessing all of S3's * functionality * - To not require the developer using libs3 to need to know anything about: * - HTTP * - XML * - SSL * In other words, this API is meant to stand on its own, without requiring * any implicit knowledge of how S3 services are accessed using HTTP * protocols. * - To be usable from multithreaded code * - To be usable by code which wants to process multiple S3 requests * simultaneously from a single thread * - To be usable in the simple, straightforward way using sequentialized * blocking requests * * The general usage pattern of libs3 is: * * - Initialize libs3 once per program by calling S3_initialize() at program * start up time * - Make any number of requests to S3 for getting, putting, or listing * S3 buckets or objects, or modifying the ACLs associated with buckets * or objects, using one of three general approaches: * 1. Simple blocking requests, one at a time * 2. Multiple threads each making simple blocking requests * 3. From a single thread, managing multiple S3 requests simultaneously * using file descriptors and a select()/poll() loop * - Shut down libs3 at program exit time by calling S3_deinitialize() * * All functions which send requests to S3 return their results via a set of * callback functions which must be supplied to libs3 at the time that the * request is initiated. libs3 will call these functions back in the thread * calling the libs3 function if blocking requests are made (i.e., if the * S3RequestContext for the function invocation is passed in as NULL). * If an S3RequestContext is used to drive multiple S3 requests * simultaneously, then the callbacks will be made from the thread which * calls S3_runall_request_context() or S3_runonce_request_context(), or * possibly from the thread which calls S3_destroy_request_context(), if * S3 requests are in progress at the time that this function is called. * * NOTE: Response headers from Amazon S3 are limited to 4K (2K of metas is all * that Amazon supports, and libs3 allows Amazon an additional 2K of headers). * * NOTE: Because HTTP and the S3 REST protocol are highly under-specified, * libs3 must make some assumptions about the maximum length of certain HTTP * elements (such as headers) that it will accept. While efforts have been * made to enforce maximums which are beyond that expected to be needed by any * user of S3, it is always possible that these maximums may be too low in * some rare circumstances. Bug reports should this unlikely situation occur * would be most appreciated. * * Threading Rules * --------------- * * 1. All arguments passed to any function must not be modified directly until * the function returns. * 2. All S3RequestContext and S3Request arguments passed to all functions may * not be passed to any other libs3 function by any other thread until the * function returns. * 3. All functions may be called simultaneously by multiple threads as long * as (1) and (2) are observed, EXCEPT for S3_initialize(), which must be * called from one thread at a time only. * 4. All callbacks will be made in the thread of the caller of the function * which invoked them, so the caller of all libs3 functions should not hold * locks that it would try to re-acquire in a callback, as this may * deadlock. ************************************************************************** **/ /** ************************************************************************** * Constants ************************************************************************** **/ /** * S3_MAX_HOSTNAME_SIZE is the maximum size we allow for a host name **/ #define S3_MAX_HOSTNAME_SIZE 255 /** * This is the default hostname that is being used for the S3 requests **/ #define S3_DEFAULT_HOSTNAME "s3.amazonaws.com" /** * S3_MAX_BUCKET_NAME_SIZE is the maximum size of a bucket name. **/ #define S3_MAX_BUCKET_NAME_SIZE 255 /** * S3_MAX_KEY_SIZE is the maximum size of keys that Amazon S3 supports. **/ #define S3_MAX_KEY_SIZE 1024 /** * S3_MAX_METADATA_SIZE is the maximum number of bytes allowed for * x-amz-meta header names and values in any request passed to Amazon S3 **/ #define S3_MAX_METADATA_SIZE 2048 /** * S3_METADATA_HEADER_NAME_PREFIX is the prefix of an S3 "meta header" **/ #define S3_METADATA_HEADER_NAME_PREFIX "x-amz-meta-" /** * S3_MAX_METADATA_COUNT is the maximum number of x-amz-meta- headers that * could be included in a request to S3. The smallest meta header is * "x-amz-meta-n: v". Since S3 doesn't count the ": " against the total, the * smallest amount of data to count for a header would be the length of * "x-amz-meta-nv". **/ #define S3_MAX_METADATA_COUNT \ (S3_MAX_METADATA_SIZE / (sizeof(S3_METADATA_HEADER_NAME_PREFIX "nv") - 1)) /** * S3_MAX_ACL_GRANT_COUNT is the maximum number of ACL grants that may be * set on a bucket or object at one time. It is also the maximum number of * ACL grants that the XML ACL parsing routine will parse. **/ #define S3_MAX_ACL_GRANT_COUNT 100 /** * This is the maximum number of characters (including terminating \0) that * libs3 supports in an ACL grantee email address. **/ #define S3_MAX_GRANTEE_EMAIL_ADDRESS_SIZE 128 /** * This is the maximum number of characters (including terminating \0) that * libs3 supports in an ACL grantee user id. **/ #define S3_MAX_GRANTEE_USER_ID_SIZE 128 /** * This is the maximum number of characters (including terminating \0) that * libs3 supports in an ACL grantee user display name. **/ #define S3_MAX_GRANTEE_DISPLAY_NAME_SIZE 128 /** * This is the maximum number of characters that will be stored in the * return buffer for the utility function which computes an HTTP authenticated * query string **/ #define S3_MAX_AUTHENTICATED_QUERY_STRING_SIZE \ (sizeof("https:///") + S3_MAX_HOSTNAME_SIZE + (S3_MAX_KEY_SIZE * 3) + \ sizeof("?AWSAccessKeyId=") + 32 + sizeof("&Expires=") + 32 + \ sizeof("&Signature=") + 28 + 1) /** * This constant is used by the S3_initialize() function, to specify that * the winsock library should be initialized by libs3; only relevent on * Microsoft Windows platforms. **/ #define S3_INIT_WINSOCK 1 /** * This convenience constant is used by the S3_initialize() function to * indicate that all libraries required by libs3 should be initialized. **/ #define S3_INIT_ALL (S3_INIT_WINSOCK) /** ************************************************************************** * Enumerations ************************************************************************** **/ /** * S3Status is a status code as returned by a libs3 function. The meaning of * each status code is defined in the comments for each function which returns * that status. **/ typedef enum { S3StatusOK , /** * Errors that prevent the S3 request from being issued or response from * being read **/ S3StatusInternalError , S3StatusOutOfMemory , S3StatusInterrupted , S3StatusInvalidBucketNameTooLong , S3StatusInvalidBucketNameFirstCharacter , S3StatusInvalidBucketNameCharacter , S3StatusInvalidBucketNameCharacterSequence , S3StatusInvalidBucketNameTooShort , S3StatusInvalidBucketNameDotQuadNotation , S3StatusQueryParamsTooLong , S3StatusFailedToInitializeRequest , S3StatusMetaDataHeadersTooLong , S3StatusBadMetaData , S3StatusBadContentType , S3StatusContentTypeTooLong , S3StatusBadMD5 , S3StatusMD5TooLong , S3StatusBadCacheControl , S3StatusCacheControlTooLong , S3StatusBadContentDispositionFilename , S3StatusContentDispositionFilenameTooLong , S3StatusBadContentEncoding , S3StatusContentEncodingTooLong , S3StatusBadIfMatchETag , S3StatusIfMatchETagTooLong , S3StatusBadIfNotMatchETag , S3StatusIfNotMatchETagTooLong , S3StatusHeadersTooLong , S3StatusKeyTooLong , S3StatusUriTooLong , S3StatusXmlParseFailure , S3StatusEmailAddressTooLong , S3StatusUserIdTooLong , S3StatusUserDisplayNameTooLong , S3StatusGroupUriTooLong , S3StatusPermissionTooLong , S3StatusTargetBucketTooLong , S3StatusTargetPrefixTooLong , S3StatusTooManyGrants , S3StatusBadGrantee , S3StatusBadPermission , S3StatusXmlDocumentTooLarge , S3StatusNameLookupError , S3StatusFailedToConnect , S3StatusServerFailedVerification , S3StatusConnectionFailed , S3StatusAbortedByCallback , /** * Errors from the S3 service **/ S3StatusErrorAccessDenied , S3StatusErrorAccountProblem , S3StatusErrorAmbiguousGrantByEmailAddress , S3StatusErrorBadDigest , S3StatusErrorBucketAlreadyExists , S3StatusErrorBucketAlreadyOwnedByYou , S3StatusErrorBucketNotEmpty , S3StatusErrorCredentialsNotSupported , S3StatusErrorCrossLocationLoggingProhibited , S3StatusErrorEntityTooSmall , S3StatusErrorEntityTooLarge , S3StatusErrorExpiredToken , S3StatusErrorIncompleteBody , S3StatusErrorIncorrectNumberOfFilesInPostRequest , S3StatusErrorInlineDataTooLarge , S3StatusErrorInternalError , S3StatusErrorInvalidAccessKeyId , S3StatusErrorInvalidAddressingHeader , S3StatusErrorInvalidArgument , S3StatusErrorInvalidBucketName , S3StatusErrorInvalidDigest , S3StatusErrorInvalidLocationConstraint , S3StatusErrorInvalidPayer , S3StatusErrorInvalidPolicyDocument , S3StatusErrorInvalidRange , S3StatusErrorInvalidSecurity , S3StatusErrorInvalidSOAPRequest , S3StatusErrorInvalidStorageClass , S3StatusErrorInvalidTargetBucketForLogging , S3StatusErrorInvalidToken , S3StatusErrorInvalidURI , S3StatusErrorKeyTooLong , S3StatusErrorMalformedACLError , S3StatusErrorMalformedXML , S3StatusErrorMaxMessageLengthExceeded , S3StatusErrorMaxPostPreDataLengthExceededError , S3StatusErrorMetadataTooLarge , S3StatusErrorMethodNotAllowed , S3StatusErrorMissingAttachment , S3StatusErrorMissingContentLength , S3StatusErrorMissingSecurityElement , S3StatusErrorMissingSecurityHeader , S3StatusErrorNoLoggingStatusForKey , S3StatusErrorNoSuchBucket , S3StatusErrorNoSuchKey , S3StatusErrorNotImplemented , S3StatusErrorNotSignedUp , S3StatusErrorOperationAborted , S3StatusErrorPermanentRedirect , S3StatusErrorPreconditionFailed , S3StatusErrorRedirect , S3StatusErrorRequestIsNotMultiPartContent , S3StatusErrorRequestTimeout , S3StatusErrorRequestTimeTooSkewed , S3StatusErrorRequestTorrentOfBucketError , S3StatusErrorSignatureDoesNotMatch , S3StatusErrorSlowDown , S3StatusErrorTemporaryRedirect , S3StatusErrorTokenRefreshRequired , S3StatusErrorTooManyBuckets , S3StatusErrorUnexpectedContent , S3StatusErrorUnresolvableGrantByEmailAddress , S3StatusErrorUserKeyMustBeSpecified , S3StatusErrorUnknown , /** * The following are HTTP errors returned by S3 without enough detail to * distinguish any of the above S3StatusError conditions **/ S3StatusHttpErrorMovedTemporarily , S3StatusHttpErrorBadRequest , S3StatusHttpErrorForbidden , S3StatusHttpErrorNotFound , S3StatusHttpErrorConflict , S3StatusHttpErrorUnknown } S3Status; /** * S3Protocol represents a protocol that may be used for communicating a * request to the Amazon S3 service. * * In general, HTTPS is greatly preferred (and should be the default of any * application using libs3) because it protects any data being sent to or * from S3 using strong encryption. However, HTTPS is much more CPU intensive * than HTTP, and if the caller is absolutely certain that it is OK for the * data to be viewable by anyone in transit, then HTTP can be used. **/ typedef enum { S3ProtocolHTTPS = 0, S3ProtocolHTTP = 1 } S3Protocol; /** * S3UriStyle defines the form that an Amazon S3 URI identifying a bucket or * object can take. They are of these forms: * * Virtual Host: ${protocol}://${bucket}.s3.amazonaws.com/[${key}] * Path: ${protocol}://s3.amazonaws.com/${bucket}/[${key}] * * It is generally better to use the Virual Host URI form, because it ensures * that the bucket name used is compatible with normal HTTP GETs and POSTs of * data to/from the bucket. However, if DNS lookups for the bucket are too * slow or unreliable for some reason, Path URI form may be used. **/ typedef enum { S3UriStyleVirtualHost = 0, S3UriStylePath = 1 } S3UriStyle; /** * S3GranteeType defines the type of Grantee used in an S3 ACL Grant. * Amazon Customer By Email - identifies the Grantee using their Amazon S3 * account email address * Canonical User - identifies the Grantee by S3 User ID and Display Name, * which can only be obtained by making requests to S3, for example, by * listing owned buckets * All AWS Users - identifies all authenticated AWS users * All Users - identifies all users * Log Delivery - identifies the Amazon group responsible for writing * server access logs into buckets **/ typedef enum { S3GranteeTypeAmazonCustomerByEmail = 0, S3GranteeTypeCanonicalUser = 1, S3GranteeTypeAllAwsUsers = 2, S3GranteeTypeAllUsers = 3, S3GranteeTypeLogDelivery = 4 } S3GranteeType; /** * This is an individual permission granted to a grantee in an S3 ACL Grant. * Read permission gives the Grantee the permission to list the bucket, or * read the object or its metadata * Write permission gives the Grantee the permission to create, overwrite, or * delete any object in the bucket, and is not supported for objects * ReadACP permission gives the Grantee the permission to read the ACP for * the bucket or object; the owner of the bucket or object always has * this permission implicitly * WriteACP permission gives the Grantee the permission to overwrite the ACP * for the bucket or object; the owner of the bucket or object always has * this permission implicitly * FullControl permission gives the Grantee all permissions specified by the * Read, Write, ReadACP, and WriteACP permissions **/ typedef enum { S3PermissionRead = 0, S3PermissionWrite = 1, S3PermissionReadACP = 2, S3PermissionWriteACP = 3, S3PermissionFullControl = 4 } S3Permission; /** * S3CannedAcl is an ACL that can be specified when an object is created or * updated. Each canned ACL has a predefined value when expanded to a full * set of S3 ACL Grants. * Private canned ACL gives the owner FULL_CONTROL and no other permissions * are issued * Public Read canned ACL gives the owner FULL_CONTROL and all users Read * permission * Public Read Write canned ACL gives the owner FULL_CONTROL and all users * Read and Write permission * AuthenticatedRead canned ACL gives the owner FULL_CONTROL and authenticated * S3 users Read permission **/ typedef enum { S3CannedAclPrivate = 0, /* private */ S3CannedAclPublicRead = 1, /* public-read */ S3CannedAclPublicReadWrite = 2, /* public-read-write */ S3CannedAclAuthenticatedRead = 3 /* authenticated-read */ } S3CannedAcl; /** ************************************************************************** * Data Types ************************************************************************** **/ /** * An S3RequestContext manages multiple S3 requests simultaneously; see the * S3_XXX_request_context functions below for details **/ typedef struct S3RequestContext S3RequestContext; /** * S3NameValue represents a single Name - Value pair, used to represent either * S3 metadata associated with a key, or S3 error details. **/ typedef struct S3NameValue { /** * The name part of the Name - Value pair **/ const char *name; /** * The value part of the Name - Value pair **/ const char *value; } S3NameValue; /** * S3ResponseProperties is passed to the properties callback function which is * called when the complete response properties have been received. Some of * the fields of this structure are optional and may not be provided in the * response, and some will always be provided in the response. **/ typedef struct S3ResponseProperties { /** * This optional field identifies the request ID and may be used when * reporting problems to Amazon. **/ const char *requestId; /** * This optional field identifies the request ID and may be used when * reporting problems to Amazon. **/ const char *requestId2; /** * This optional field is the content type of the data which is returned * by the request. If not provided, the default can be assumed to be * "binary/octet-stream". **/ const char *contentType; /** * This optional field is the content length of the data which is returned * in the response. A negative value means that this value was not * provided in the response. A value of 0 means that there is no content * provided. A positive value gives the number of bytes in the content of * the response. **/ uint64_t contentLength; /** * This optional field names the server which serviced the request. **/ const char *server; /** * This optional field provides a string identifying the unique contents * of the resource identified by the request, such that the contents can * be assumed not to be changed if the same eTag is returned at a later * time decribing the same resource. This is an MD5 sum of the contents. **/ const char *eTag; /** * This optional field provides the last modified time, relative to the * Unix epoch, of the contents. If this value is < 0, then the last * modified time was not provided in the response. If this value is >= 0, * then the last modified date of the contents are available as a number * of seconds since the UNIX epoch. * **/ int64_t lastModified; /** * This is the number of user-provided meta data associated with the * resource. **/ int metaDataCount; /** * These are the meta data associated with the resource. In each case, * the name will not include any S3-specific header prefixes * (i.e. x-amz-meta- will have been removed from the beginning), and * leading and trailing whitespace will have been stripped from the value. **/ const S3NameValue *metaData; } S3ResponseProperties; /** * S3AclGrant identifies a single grant in the ACL for a bucket or object. An * ACL is composed of any number of grants, which specify a grantee and the * permissions given to that grantee. S3 does not normalize ACLs in any way, * so a redundant ACL specification will lead to a redundant ACL stored in S3. **/ typedef struct S3AclGrant { /** * The granteeType gives the type of grantee specified by this grant. **/ S3GranteeType granteeType; /** * The identifier of the grantee that is set is determined by the * granteeType: * * S3GranteeTypeAmazonCustomerByEmail - amazonCustomerByEmail.emailAddress * S3GranteeTypeCanonicalUser - canonicalUser.id, canonicalUser.displayName * S3GranteeTypeAllAwsUsers - none * S3GranteeTypeAllUsers - none **/ union { /** * This structure is used iff the granteeType is * S3GranteeTypeAmazonCustomerByEmail. **/ struct { /** * This is the email address of the Amazon Customer being granted * permissions by this S3AclGrant. **/ char emailAddress[S3_MAX_GRANTEE_EMAIL_ADDRESS_SIZE]; } amazonCustomerByEmail; /** * This structure is used iff the granteeType is * S3GranteeTypeCanonicalUser. **/ struct { /** * This is the CanonicalUser ID of the grantee **/ char id[S3_MAX_GRANTEE_USER_ID_SIZE]; /** * This is the display name of the grantee **/ char displayName[S3_MAX_GRANTEE_DISPLAY_NAME_SIZE]; } canonicalUser; } grantee; /** * This is the S3Permission to be granted to the grantee **/ S3Permission permission; } S3AclGrant; /** * A context for working with objects within a bucket. A bucket context holds * all information necessary for working with a bucket, and may be used * repeatedly over many consecutive (or simultaneous) calls into libs3 bucket * operation functions. **/ typedef struct S3BucketContext { /** * The name of the host to connect to when making S3 requests. If set to * NULL, the default S3 hostname passed in to S3_initialize will be used. **/ const char *hostName; /** * The name of the bucket to use in the bucket context **/ const char *bucketName; /** * The protocol to use when accessing the bucket **/ S3Protocol protocol; /** * The URI style to use for all URIs sent to Amazon S3 while working with * this bucket context **/ S3UriStyle uriStyle; /** * The Amazon Access Key ID to use for access to the bucket **/ const char *accessKeyId; /** * The Amazon Secret Access Key to use for access to the bucket **/ const char *secretAccessKey; } S3BucketContext; /** * This is a single entry supplied to the list bucket callback by a call to * S3_list_bucket. It identifies a single matching key from the list * operation. **/ typedef struct S3ListBucketContent { /** * This is the next key in the list bucket results. **/ const char *key; /** * This is the number of seconds since UNIX epoch of the last modified * date of the object identified by the key. **/ int64_t lastModified; /** * This gives a tag which gives a signature of the contents of the object, * which is the MD5 of the contents of the object. **/ const char *eTag; /** * This is the size of the object in bytes. **/ uint64_t size; /** * This is the ID of the owner of the key; it is present only if access * permissions allow it to be viewed. **/ const char *ownerId; /** * This is the display name of the owner of the key; it is present only if * access permissions allow it to be viewed. **/ const char *ownerDisplayName; } S3ListBucketContent; /** * S3PutProperties is the set of properties that may optionally be set by the * user when putting objects to S3. Each field of this structure is optional * and may or may not be present. **/ typedef struct S3PutProperties { /** * If present, this is the Content-Type that should be associated with the * object. If not provided, S3 defaults to "binary/octet-stream". **/ const char *contentType; /** * If present, this provides the MD5 signature of the contents, and is * used to validate the contents. This is highly recommended by Amazon * but not required. Its format is as a base64-encoded MD5 sum. **/ const char *md5; /** * If present, this gives a Cache-Control header string to be supplied to * HTTP clients which download this **/ const char *cacheControl; /** * If present, this gives the filename to save the downloaded file to, * whenever the object is downloaded via a web browser. This is only * relevent for objects which are intended to be shared to users via web * browsers and which is additionally intended to be downloaded rather * than viewed. **/ const char *contentDispositionFilename; /** * If present, this identifies the content encoding of the object. This * is only applicable to encoded (usually, compressed) content, and only * relevent if the object is intended to be downloaded via a browser. **/ const char *contentEncoding; /** * If >= 0, this gives an expiration date for the content. This * information is typically only delivered to users who download the * content via a web browser. **/ int64_t expires; /** * This identifies the "canned ACL" that should be used for this object. * The default (0) gives only the owner of the object access to it. **/ S3CannedAcl cannedAcl; /** * This is the number of values in the metaData field. **/ int metaDataCount; /** * These are the meta data to pass to S3. In each case, the name part of * the Name - Value pair should not include any special S3 HTTP header * prefix (i.e., should be of the form 'foo', NOT 'x-amz-meta-foo'). **/ const S3NameValue *metaData; } S3PutProperties; /** * S3GetConditions is used for the get_object operation, and specifies * conditions which the object must meet in order to be successfully returned. **/ typedef struct S3GetConditions { /** * The request will be processed if the Last-Modification header of the * object is greater than or equal to this value, specified as a number of * seconds since Unix epoch. If this value is less than zero, it will not * be used in the conditional. **/ int64_t ifModifiedSince; /** * The request will be processed if the Last-Modification header of the * object is less than this value, specified as a number of seconds since * Unix epoch. If this value is less than zero, it will not be used in * the conditional. **/ int64_t ifNotModifiedSince; /** * If non-NULL, this gives an eTag header value which the object must * match in order to be returned. Note that altough the eTag is simply an * MD5, this must be presented in the S3 eTag form, which typically * includes double-quotes. **/ const char *ifMatchETag; /** * If non-NULL, this gives an eTag header value which the object must not * match in order to be returned. Note that altough the eTag is simply an * MD5, this must be presented in the S3 eTag form, which typically * includes double-quotes. **/ const char *ifNotMatchETag; } S3GetConditions; /** * S3ErrorDetails provides detailed information describing an S3 error. This * is only presented when the error is an S3-generated error (i.e. one of the * S3StatusErrorXXX values). **/ typedef struct S3ErrorDetails { /** * This is the human-readable message that Amazon supplied describing the * error **/ const char *message; /** * This identifies the resource for which the error occurred **/ const char *resource; /** * This gives human-readable further details describing the specifics of * this error **/ const char *furtherDetails; /** * This gives the number of S3NameValue pairs present in the extraDetails * array **/ int extraDetailsCount; /** * S3 can provide extra details in a freeform Name - Value pair format. * Each error can have any number of these, and this array provides these * additional extra details. **/ S3NameValue *extraDetails; } S3ErrorDetails; /** ************************************************************************** * Callback Signatures ************************************************************************** **/ /** * This callback is made whenever the response properties become available for * any request. * * @param properties are the properties that are available from the response * @param callbackData is the callback data as specified when the request * was issued. * @return S3StatusOK to continue processing the request, anything else to * immediately abort the request with a status which will be * passed to the S3ResponseCompleteCallback for this request. * Typically, this will return either S3StatusOK or * S3StatusAbortedByCallback. **/ typedef S3Status (S3ResponsePropertiesCallback) (const S3ResponseProperties *properties, void *callbackData); /** * This callback is made when the response has been completely received, or an * error has occurred which has prematurely aborted the request, or one of the * other user-supplied callbacks returned a value intended to abort the * request. This callback is always made for every request, as the very last * callback made for that request. * * @param status gives the overall status of the response, indicating success * or failure; use S3_status_is_retryable() as a simple way to detect * whether or not the status indicates that the request failed but may * be retried. * @param errorDetails if non-NULL, gives details as returned by the S3 * service, describing the error * @param callbackData is the callback data as specified when the request * was issued. **/ typedef void (S3ResponseCompleteCallback)(S3Status status, const S3ErrorDetails *errorDetails, void *callbackData); /** * This callback is made for each bucket resulting from a list service * operation. * * @param ownerId is the ID of the owner of the bucket * @param ownerDisplayName is the owner display name of the owner of the bucket * @param bucketName is the name of the bucket * @param creationDateSeconds if < 0 indicates that no creation date was * supplied for the bucket; if >= 0 indicates the number of seconds * since UNIX Epoch of the creation date of the bucket * @param callbackData is the callback data as specified when the request * was issued. * @return S3StatusOK to continue processing the request, anything else to * immediately abort the request with a status which will be * passed to the S3ResponseCompleteCallback for this request. * Typically, this will return either S3StatusOK or * S3StatusAbortedByCallback. **/ typedef S3Status (S3ListServiceCallback)(const char *ownerId, const char *ownerDisplayName, const char *bucketName, int64_t creationDateSeconds, void *callbackData); /** * This callback is made repeatedly as a list bucket operation progresses. * The contents reported via this callback are only reported once per list * bucket operation, but multiple calls to this callback may be necessary to * report all items resulting from the list bucket operation. * * @param isTruncated is true if the list bucket request was truncated by the * S3 service, in which case the remainder of the list may be obtained * by querying again using the Marker parameter to start the query * after this set of results * @param nextMarker if present, gives the largest (alphabetically) key * returned in the response, which, if isTruncated is true, may be used * as the marker in a subsequent list buckets operation to continue * listing * @param contentsCount is the number of ListBucketContent structures in the * contents parameter * @param contents is an array of ListBucketContent structures, each one * describing an object in the bucket * @param commonPrefixesCount is the number of common prefixes strings in the * commonPrefixes parameter * @param commonPrefixes is an array of strings, each specifing one of the * common prefixes as returned by S3 * @param callbackData is the callback data as specified when the request * was issued. * @return S3StatusOK to continue processing the request, anything else to * immediately abort the request with a status which will be * passed to the S3ResponseCompleteCallback for this request. * Typically, this will return either S3StatusOK or * S3StatusAbortedByCallback. **/ typedef S3Status (S3ListBucketCallback)(int isTruncated, const char *nextMarker, int contentsCount, const S3ListBucketContent *contents, int commonPrefixesCount, const char **commonPrefixes, void *callbackData); /** * This callback is made during a put object operation, to obtain the next * chunk of data to put to the S3 service as the contents of the object. This * callback is made repeatedly, each time acquiring the next chunk of data to * write to the service, until a negative or 0 value is returned. * * @param bufferSize gives the maximum number of bytes that may be written * into the buffer parameter by this callback * @param buffer gives the buffer to fill with at most bufferSize bytes of * data as the next chunk of data to send to S3 as the contents of this * object * @param callbackData is the callback data as specified when the request * was issued. * @return < 0 to abort the request with the S3StatusAbortedByCallback, which * will be pased to the response complete callback for this request, or * 0 to indicate the end of data, or > 0 to identify the number of * bytes that were written into the buffer by this callback **/ typedef int (S3PutObjectDataCallback)(int bufferSize, char *buffer, void *callbackData); /** * This callback is made during a get object operation, to provide the next * chunk of data available from the S3 service constituting the contents of * the object being fetched. This callback is made repeatedly, each time * providing the next chunk of data read, until the complete object contents * have been passed through the callback in this way, or the callback * returns an error status. * * @param bufferSize gives the number of bytes in buffer * @param buffer is the data being passed into the callback * @param callbackData is the callback data as specified when the request * was issued. * @return S3StatusOK to continue processing the request, anything else to * immediately abort the request with a status which will be * passed to the S3ResponseCompleteCallback for this request. * Typically, this will return either S3StatusOK or * S3StatusAbortedByCallback. **/ typedef S3Status (S3GetObjectDataCallback)(int bufferSize, const char *buffer, void *callbackData); /** ************************************************************************** * Callback Structures ************************************************************************** **/ /** * An S3ResponseHandler defines the callbacks which are made for any * request. **/ typedef struct S3ResponseHandler { /** * The propertiesCallback is made when the response properties have * successfully been returned from S3. This function may not be called * if the response properties were not successfully returned from S3. **/ S3ResponsePropertiesCallback *propertiesCallback; /** * The completeCallback is always called for every request made to S3, * regardless of the outcome of the request. It provides the status of * the request upon its completion, as well as extra error details in the * event of an S3 error. **/ S3ResponseCompleteCallback *completeCallback; } S3ResponseHandler; /** * An S3ListServiceHandler defines the callbacks which are made for * list_service requests. **/ typedef struct S3ListServiceHandler { /** * responseHandler provides the properties and complete callback **/ S3ResponseHandler responseHandler; /** * The listServiceCallback is called as items are reported back from S3 as * responses to the request **/ S3ListServiceCallback *listServiceCallback; } S3ListServiceHandler; /** * An S3ListBucketHandler defines the callbacks which are made for * list_bucket requests. **/ typedef struct S3ListBucketHandler { /** * responseHandler provides the properties and complete callback **/ S3ResponseHandler responseHandler; /** * The listBucketCallback is called as items are reported back from S3 as * responses to the request. This may be called more than one time per * list bucket request, each time providing more items from the list * operation. **/ S3ListBucketCallback *listBucketCallback; } S3ListBucketHandler; /** * An S3PutObjectHandler defines the callbacks which are made for * put_object requests. **/ typedef struct S3PutObjectHandler { /** * responseHandler provides the properties and complete callback **/ S3ResponseHandler responseHandler; /** * The putObjectDataCallback is called to acquire data to send to S3 as * the contents of the put_object request. It is made repeatedly until it * returns a negative number (indicating that the request should be * aborted), or 0 (indicating that all data has been supplied). **/ S3PutObjectDataCallback *putObjectDataCallback; } S3PutObjectHandler; /** * An S3GetObjectHandler defines the callbacks which are made for * get_object requests. **/ typedef struct S3GetObjectHandler { /** * responseHandler provides the properties and complete callback **/ S3ResponseHandler responseHandler; /** * The getObjectDataCallback is called as data is read from S3 as the * contents of the object being read in the get_object request. It is * called repeatedly until there is no more data provided in the request, * or until the callback returns an error status indicating that the * request should be aborted. **/ S3GetObjectDataCallback *getObjectDataCallback; } S3GetObjectHandler; /** ************************************************************************** * General Library Functions ************************************************************************** **/ /** * Initializes libs3 for use. This function must be called before any other * libs3 function is called. It may be called multiple times, with the same * effect as calling it once, as long as S3_deinitialize() is called an * equal number of times when the program has finished. This function is NOT * thread-safe and must only be called by one thread at a time. * * @param userAgentInfo is a string that will be included in the User-Agent * header of every request made to the S3 service. You may provide * NULL or the empty string if you don't care about this. The value * will not be copied by this function and must remain unaltered by the * caller until S3_deinitialize() is called. * @param flags is a bitmask of some combination of S3_INIT_XXX flag, or * S3_INIT_ALL, indicating which of the libraries that libs3 depends * upon should be initialized by S3_initialize(). Only if your program * initializes one of these dependency libraries itself should anything * other than S3_INIT_ALL be passed in for this bitmask. * * You should pass S3_INIT_WINSOCK if and only if your application does * not initialize winsock elsewhere. On non-Microsoft Windows * platforms it has no effect. * * As a convenience, the macro S3_INIT_ALL is provided, which will do * all necessary initialization; however, be warned that things may * break if your application re-initializes the dependent libraries * later. * @param defaultS3Hostname is a string the specifies the default S3 server * hostname to use when making S3 requests; this value is used * whenever the hostName of an S3BucketContext is NULL. If NULL is * passed here then the default of S3_DEFAULT_HOSTNAME will be used. * @return One of: * S3StatusOK on success * S3StatusUriTooLong if the defaultS3HostName is longer than * S3_MAX_HOSTNAME_SIZE * S3StatusInternalError if dependent libraries could not be * initialized * S3StatusOutOfMemory on failure due to out of memory **/ S3Status S3_initialize(const char *userAgentInfo, int flags, const char *defaultS3HostName); /** * Must be called once per program for each call to libs3_initialize(). After * this call is complete, no libs3 function may be called except * S3_initialize(). **/ void S3_deinitialize(); /** * Returns a string with the textual name of an S3Status code * * @param status is S3Status code for which the textual name will be returned * @return a string with the textual name of an S3Status code **/ const char *S3_get_status_name(S3Status status); /** * This function may be used to validate an S3 bucket name as being in the * correct form for use with the S3 service. Amazon S3 limits the allowed * characters in S3 bucket names, as well as imposing some additional rules on * the length of bucket names and their structure. There are actually two * limits; one for bucket names used only in path-style URIs, and a more * strict limit used for bucket names used in virtual-host-style URIs. It is * advisable to use only bucket names which meet the more strict requirements * regardless of how the bucket expected to be used. * * This method does NOT validate that the bucket is available for use in the * S3 service, so the return value of this function cannot be used to decide * whether or not a bucket with the give name already exists in Amazon S3 or * is accessible by the caller. It merely validates that the bucket name is * valid for use with S3. * * @param bucketName is the bucket name to validate * @param uriStyle gives the URI style to validate the bucket name against. * It is advisable to always use S3UriStyleVirtuallHost. * @return One of: * S3StatusOK if the bucket name was validates successfully * S3StatusInvalidBucketNameTooLong if the bucket name exceeded the * length limitation for the URI style, which is 255 bytes for * path style URIs and 63 bytes for virtual host type URIs * S3StatusInvalidBucketNameTooShort if the bucket name is less than * 3 characters * S3StatusInvalidBucketNameFirstCharacter if the bucket name as an * invalid first character, which is anything other than * an alphanumeric character * S3StatusInvalidBucketNameCharacterSequence if the bucket name * includes an invalid character sequence, which for virtual host * style buckets is ".-" or "-." * S3StatusInvalidBucketNameCharacter if the bucket name includes an * invalid character, which is anything other than alphanumeric, * '-', '.', or for path style URIs only, '_'. * S3StatusInvalidBucketNameDotQuadNotation if the bucket name is in * dot-quad notation, i.e. the form of an IP address, which is * not allowed by Amazon S3. **/ S3Status S3_validate_bucket_name(const char *bucketName, S3UriStyle uriStyle); /** * Converts an XML representation of an ACL to a libs3 structured * representation. This method is not strictly necessary for working with * ACLs using libs3, but may be convenient for users of the library who read * ACLs from elsewhere in XML format and need to use these ACLs with libs3. * * @param aclXml is the XML representation of the ACL. This must be a * zero-terminated character string. * @param ownerId will be filled in with the Owner ID specified in the XML. * At most MAX_GRANTEE_USER_ID_SIZE bytes will be stored at this * location. * @param ownerDisplayName will be filled in with the Owner Display Name * specified in the XML. At most MAX_GRANTEE_DISPLAY_NAME_SIZE bytes * will be stored at this location. * @param aclGrantCountReturn returns the number of S3AclGrant structures * returned in the aclGrantsReturned array * @param aclGrants must be passed in as an array of at least S3_ACL_MAXCOUNT * structures, and on return from this function, the first * aclGrantCountReturn structures will be filled in with the ACLs * represented by the input XML. * @return One of: * S3StatusOK on successful conversion of the ACL * S3StatusInternalError on internal error representing a bug in the * libs3 library * S3StatusXmlParseFailure if the XML document was malformed **/ S3Status S3_convert_acl(char *aclXml, char *ownerId, char *ownerDisplayName, int *aclGrantCountReturn, S3AclGrant *aclGrants); /** * Returns nonzero if the status indicates that the request should be * immediately retried, because the status indicates an error of a nature that * is likely due to transient conditions on the local system or S3, such as * network failures, or internal retryable errors reported by S3. Returns * zero otherwise. * * @param status is the status to evaluate * @return nonzero if the status indicates a retryable error, 0 otherwise **/ int S3_status_is_retryable(S3Status status); /** ************************************************************************** * Request Context Management Functions ************************************************************************** **/ /** * An S3RequestContext allows muliple requests to be serviced by the same * thread simultaneously. It is an optional parameter to all libs3 request * functions, and if provided, the request is managed by the S3RequestContext; * if not, the request is handled synchronously and is complete when the libs3 * request function has returned. * * @param requestContextReturn returns the newly-created S3RequestContext * structure, which if successfully returned, must be destroyed via a * call to S3_destroy_request_context when it is no longer needed. If * an error status is returned from this function, then * requestContextReturn will not have been filled in, and * S3_destroy_request_context should not be called on it * @return One of: * S3StatusOK if the request context was successfully created * S3StatusOutOfMemory if the request context could not be created due * to an out of memory error **/ S3Status S3_create_request_context(S3RequestContext **requestContextReturn); /** * Destroys an S3RequestContext which was created with * S3_create_request_context. Any requests which are currently being * processed by the S3RequestContext will immediately be aborted and their * request completed callbacks made with the status S3StatusInterrupted. * * @param requestContext is the S3RequestContext to destroy **/ void S3_destroy_request_context(S3RequestContext *requestContext); /** * Runs the S3RequestContext until all requests within it have completed, * or until an error occurs. * * @param requestContext is the S3RequestContext to run until all requests * within it have completed or until an error occurs * @return One of: * S3Status if all requests were successfully run to completion * S3StatusInternalError if an internal error prevented the * S3RequestContext from running one or more requests * S3StatusOutOfMemory if requests could not be run to completion * due to an out of memory error **/ S3Status S3_runall_request_context(S3RequestContext *requestContext); /** * Does some processing of requests within the S3RequestContext. One or more * requests may have callbacks made on them and may complete. This function * processes any requests which have immediately available I/O, and will not * block waiting for I/O on any request. This function would normally be used * with S3_get_request_context_fdsets. * * @param requestContext is the S3RequestContext to process * @param requestsRemainingReturn returns the number of requests remaining * and not yet completed within the S3RequestContext after this * function returns. * @return One of: * S3StatusOK if request processing proceeded without error * S3StatusInternalError if an internal error prevented the * S3RequestContext from running one or more requests * S3StatusOutOfMemory if requests could not be processed due to * an out of memory error **/ S3Status S3_runonce_request_context(S3RequestContext *requestContext, int *requestsRemainingReturn); /** * This function, in conjunction allows callers to manually manage a set of * requests using an S3RequestContext. This function returns the set of file * descriptors which the caller can watch (typically using select()), along * with any other file descriptors of interest to the caller, and using * whatever timeout (if any) the caller wishes, until one or more file * descriptors in the returned sets become ready for I/O, at which point * S3_runonce_request_context can be called to process requests with available * I/O. * * @param requestContext is the S3RequestContext to get fd_sets from * @param readFdSet is a pointer to an fd_set which will have all file * descriptors to watch for read events for the requests in the * S3RequestContext set into it upon return. Should be zero'd out * (using FD_ZERO) before being passed into this function. * @param writeFdSet is a pointer to an fd_set which will have all file * descriptors to watch for write events for the requests in the * S3RequestContext set into it upon return. Should be zero'd out * (using FD_ZERO) before being passed into this function. * @param exceptFdSet is a pointer to an fd_set which will have all file * descriptors to watch for exception events for the requests in the * S3RequestContext set into it upon return. Should be zero'd out * (using FD_ZERO) before being passed into this function. * @param maxFd returns the highest file descriptor set into any of the * fd_sets, or -1 if no file descriptors were set * @return One of: * S3StatusOK if all fd_sets were successfully set * S3StatusInternalError if an internal error prevented this function * from completing successfully **/ S3Status S3_get_request_context_fdsets(S3RequestContext *requestContext, fd_set *readFdSet, fd_set *writeFdSet, fd_set *exceptFdSet, int *maxFd); /** * This function returns the maximum number of milliseconds that the caller of * S3_runonce_request_context should wait on the fdsets obtained via a call to * S3_get_request_context_fdsets. In other words, this is essentially the * select() timeout that needs to be used (shorter values are OK, but no * longer than this) to ensure that internal timeout code of libs3 can work * properly. This function should be called right before select() each time * select() on the request_context fdsets are to be performed by the libs3 * user. * * @param requestContext is the S3RequestContext to get the timeout from * @return the maximum number of milliseconds to select() on fdsets. Callers * could wait a shorter time if they wish, but not longer. **/ int64_t S3_get_request_context_timeout(S3RequestContext *requestContext); /** ************************************************************************** * S3 Utility Functions ************************************************************************** **/ /** * Generates an HTTP authenticated query string, which may then be used by * a browser (or other web client) to issue the request. The request is * implicitly a GET request; Amazon S3 is documented to only support this type * of authenticated query string request. * * @param buffer is the output buffer for the authenticated query string. * It must be at least S3_MAX_AUTHENTICATED_QUERY_STRING_SIZE bytes in * length. * @param bucketContext gives the bucket and associated parameters for the * request to generate. * @param key gives the key which the authenticated request will GET. * @param expires gives the number of seconds since Unix epoch for the * expiration date of the request; after this time, the request will * no longer be valid. If this value is negative, the largest * expiration date possible is used (currently, Jan 19, 2038). * @param resource gives a sub-resource to be fetched for the request, or NULL * for none. This should be of the form "?", i.e. * "?torrent". * @return One of: * S3StatusUriTooLong if, due to an internal error, the generated URI * is longer than S3_MAX_AUTHENTICATED_QUERY_STRING_SIZE bytes in * length and thus will not fit into the supplied buffer * S3StatusOK on success **/ S3Status S3_generate_authenticated_query_string (char *buffer, const S3BucketContext *bucketContext, const char *key, int64_t expires, const char *resource); /** ************************************************************************** * Service Functions ************************************************************************** **/ /** * Lists all S3 buckets belonging to the access key id. * * @param protocol gives the protocol to use for this request * @param accessKeyId gives the Amazon Access Key ID for which to list owned * buckets * @param secretAccessKey gives the Amazon Secret Access Key for which to list * owned buckets * @param hostName is the S3 host name to use; if NULL is passed in, the * default S3 host as provided to S3_initialize() will be used. * @param requestContext if non-NULL, gives the S3RequestContext to add this * request to, and does not perform the request immediately. If NULL, * performs the request immediately and synchronously. * @param handler gives the callbacks to call as the request is processed and * completed * @param callbackData will be passed in as the callbackData parameter to * all callbacks for this request **/ void S3_list_service(S3Protocol protocol, const char *accessKeyId, const char *secretAccessKey, const char *hostName, S3RequestContext *requestContext, const S3ListServiceHandler *handler, void *callbackData); /** ************************************************************************** * Bucket Functions ************************************************************************** **/ /** * Tests the existence of an S3 bucket, additionally returning the bucket's * location if it exists and is accessible. * * @param protocol gives the protocol to use for this request * @param uriStyle gives the URI style to use for this request * @param accessKeyId gives the Amazon Access Key ID for which to list owned * buckets * @param secretAccessKey gives the Amazon Secret Access Key for which to list * owned buckets * @param hostName is the S3 host name to use; if NULL is passed in, the * default S3 host as provided to S3_initialize() will be used. * @param bucketName is the bucket name to test * @param locationConstraintReturnSize gives the number of bytes in the * locationConstraintReturn parameter * @param locationConstraintReturn provides the location into which to write * the name of the location constraint naming the geographic location * of the S3 bucket. This must have at least as many characters in it * as specified by locationConstraintReturn, and should start out * NULL-terminated. On successful completion of this request, this * will be set to the name of the geographic location of S3 bucket, or * will be left as a zero-length string if no location was available. * @param requestContext if non-NULL, gives the S3RequestContext to add this * request to, and does not perform the request immediately. If NULL, * performs the request immediately and synchronously. * @param handler gives the callbacks to call as the request is processed and * completed * @param callbackData will be passed in as the callbackData parameter to * all callbacks for this request **/ void S3_test_bucket(S3Protocol protocol, S3UriStyle uriStyle, const char *accessKeyId, const char *secretAccessKey, const char *hostName, const char *bucketName, int locationConstraintReturnSize, char *locationConstraintReturn, S3RequestContext *requestContext, const S3ResponseHandler *handler, void *callbackData); /** * Creates a new bucket. * * @param protocol gives the protocol to use for this request * @param accessKeyId gives the Amazon Access Key ID for which to list owned * buckets * @param secretAccessKey gives the Amazon Secret Access Key for which to list * owned buckets * @param hostName is the S3 host name to use; if NULL is passed in, the * default S3 host as provided to S3_initialize() will be used. * @param bucketName is the name of the bucket to be created * @param cannedAcl gives the "REST canned ACL" to use for the created bucket * @param locationConstraint if non-NULL, gives the geographic location for * the bucket to create. * @param requestContext if non-NULL, gives the S3RequestContext to add this * request to, and does not perform the request immediately. If NULL, * performs the request immediately and synchronously. * @param handler gives the callbacks to call as the request is processed and * completed * @param callbackData will be passed in as the callbackData parameter to * all callbacks for this request **/ void S3_create_bucket(S3Protocol protocol, const char *accessKeyId, const char *secretAccessKey, const char *hostName, const char *bucketName, S3CannedAcl cannedAcl, const char *locationConstraint, S3RequestContext *requestContext, const S3ResponseHandler *handler, void *callbackData); /** * Deletes a bucket. The bucket must be empty, or the status * S3StatusErrorBucketNotEmpty will result. * * @param protocol gives the protocol to use for this request * @param uriStyle gives the URI style to use for this request * @param accessKeyId gives the Amazon Access Key ID for which to list owned * buckets * @param secretAccessKey gives the Amazon Secret Access Key for which to list * owned buckets * @param hostName is the S3 host name to use; if NULL is passed in, the * default S3 host as provided to S3_initialize() will be used. * @param bucketName is the name of the bucket to be deleted * @param requestContext if non-NULL, gives the S3RequestContext to add this * request to, and does not perform the request immediately. If NULL, * performs the request immediately and synchronously. * @param handler gives the callbacks to call as the request is processed and * completed * @param callbackData will be passed in as the callbackData parameter to * all callbacks for this request **/ void S3_delete_bucket(S3Protocol protocol, S3UriStyle uriStyle, const char *accessKeyId, const char *secretAccessKey, const char *hostName, const char *bucketName, S3RequestContext *requestContext, const S3ResponseHandler *handler, void *callbackData); /** * Lists keys within a bucket. * * @param bucketContext gives the bucket and associated parameters for this * request * @param prefix if present, gives a prefix for matching keys * @param marker if present, only keys occuring after this value will be * listed * @param delimiter if present, causes keys that contain the same string * between the prefix and the first occurrence of the delimiter to be * rolled up into a single result element * @param maxkeys is the maximum number of keys to return * @param requestContext if non-NULL, gives the S3RequestContext to add this * request to, and does not perform the request immediately. If NULL, * performs the request immediately and synchronously. * @param handler gives the callbacks to call as the request is processed and * completed * @param callbackData will be passed in as the callbackData parameter to * all callbacks for this request **/ void S3_list_bucket(const S3BucketContext *bucketContext, const char *prefix, const char *marker, const char *delimiter, int maxkeys, S3RequestContext *requestContext, const S3ListBucketHandler *handler, void *callbackData); /** ************************************************************************** * Object Functions ************************************************************************** **/ /** * Puts object data to S3. This overwrites any existing object at that key; * note that S3 currently only supports full-object upload. The data to * upload will be acquired by calling the handler's putObjectDataCallback. * * @param bucketContext gives the bucket and associated parameters for this * request * @param key is the key of the object to put to * @param contentLength is required and gives the total number of bytes that * will be put * @param putProperties optionally provides additional properties to apply to * the object that is being put to * @param requestContext if non-NULL, gives the S3RequestContext to add this * request to, and does not perform the request immediately. If NULL, * performs the request immediately and synchronously. * @param handler gives the callbacks to call as the request is processed and * completed * @param callbackData will be passed in as the callbackData parameter to * all callbacks for this request **/ void S3_put_object(const S3BucketContext *bucketContext, const char *key, uint64_t contentLength, const S3PutProperties *putProperties, S3RequestContext *requestContext, const S3PutObjectHandler *handler, void *callbackData); /** * Copies an object from one location to another. The object may be copied * back to itself, which is useful for replacing metadata without changing * the object. * * @param bucketContext gives the source bucket and associated parameters for * this request * @param key is the source key * @param destinationBucket gives the destination bucket into which to copy * the object. If NULL, the source bucket will be used. * @param destinationKey gives the destination key into which to copy the * object. If NULL, the source key will be used. * @param putProperties optionally provides properties to apply to the object * that is being put to. If not supplied (i.e. NULL is passed in), * then the copied object will retain the metadata of the copied * object. * @param lastModifiedReturn returns the last modified date of the copied * object * @param eTagReturnSize specifies the number of bytes provided in the * eTagReturn buffer * @param eTagReturn is a buffer into which the resulting eTag of the copied * object will be written * @param handler gives the callbacks to call as the request is processed and * completed * @param callbackData will be passed in as the callbackData parameter to * all callbacks for this request * @param requestContext if non-NULL, gives the S3RequestContext to add this * request to, and does not perform the request immediately. If NULL, * performs the request immediately and synchronously. * @param handler gives the callbacks to call as the request is processed and * completed * @param callbackData will be passed in as the callbackData parameter to * all callbacks for this request **/ void S3_copy_object(const S3BucketContext *bucketContext, const char *key, const char *destinationBucket, const char *destinationKey, const S3PutProperties *putProperties, int64_t *lastModifiedReturn, int eTagReturnSize, char *eTagReturn, S3RequestContext *requestContext, const S3ResponseHandler *handler, void *callbackData); /** * Gets an object from S3. The contents of the object are returned in the * handler's getObjectDataCallback. * * @param bucketContext gives the bucket and associated parameters for this * request * @param key is the key of the object to get * @param getConditions if non-NULL, gives a set of conditions which must be * met in order for the request to succeed * @param startByte gives the start byte for the byte range of the contents * to be returned * @param byteCount gives the number of bytes to return; a value of 0 * indicates that the contents up to the end should be returned * @param requestContext if non-NULL, gives the S3RequestContext to add this * request to, and does not perform the request immediately. If NULL, * performs the request immediately and synchronously. * @param handler gives the callbacks to call as the request is processed and * completed * @param callbackData will be passed in as the callbackData parameter to * all callbacks for this request **/ void S3_get_object(const S3BucketContext *bucketContext, const char *key, const S3GetConditions *getConditions, uint64_t startByte, uint64_t byteCount, S3RequestContext *requestContext, const S3GetObjectHandler *handler, void *callbackData); /** * Gets the response properties for the object, but not the object contents. * * @param bucketContext gives the bucket and associated parameters for this * request * @param key is the key of the object to get the properties of * @param requestContext if non-NULL, gives the S3RequestContext to add this * request to, and does not perform the request immediately. If NULL, * performs the request immediately and synchronously. * @param handler gives the callbacks to call as the request is processed and * completed * @param callbackData will be passed in as the callbackData parameter to * all callbacks for this request **/ void S3_head_object(const S3BucketContext *bucketContext, const char *key, S3RequestContext *requestContext, const S3ResponseHandler *handler, void *callbackData); /** * Deletes an object from S3. * * @param bucketContext gives the bucket and associated parameters for this * request * @param key is the key of the object to delete * @param requestContext if non-NULL, gives the S3RequestContext to add this * request to, and does not perform the request immediately. If NULL, * performs the request immediately and synchronously. * @param handler gives the callbacks to call as the request is processed and * completed * @param callbackData will be passed in as the callbackData parameter to * all callbacks for this request **/ void S3_delete_object(const S3BucketContext *bucketContext, const char *key, S3RequestContext *requestContext, const S3ResponseHandler *handler, void *callbackData); /** ************************************************************************** * Access Control List Functions ************************************************************************** **/ /** * Gets the ACL for the given bucket or object. * * @param bucketContext gives the bucket and associated parameters for this * request * @param key is the key of the object to get the ACL of; or NULL to get the * ACL of the bucket * @param ownerId must be supplied as a buffer of at least * S3_MAX_GRANTEE_USER_ID_SIZE bytes, and will be filled in with the * owner ID of the object/bucket * @param ownerDisplayName must be supplied as a buffer of at least * S3_MAX_GRANTEE_DISPLAY_NAME_SIZE bytes, and will be filled in with * the display name of the object/bucket * @param aclGrantCountReturn returns the number of S3AclGrant structures * returned in the aclGrants parameter * @param aclGrants must be passed in as an array of at least * S3_MAX_ACL_GRANT_COUNT S3AclGrant structures, which will be filled * in with the grant information for the ACL * @param requestContext if non-NULL, gives the S3RequestContext to add this * request to, and does not perform the request immediately. If NULL, * performs the request immediately and synchronously. * @param handler gives the callbacks to call as the request is processed and * completed * @param callbackData will be passed in as the callbackData parameter to * all callbacks for this request **/ void S3_get_acl(const S3BucketContext *bucketContext, const char *key, char *ownerId, char *ownerDisplayName, int *aclGrantCountReturn, S3AclGrant *aclGrants, S3RequestContext *requestContext, const S3ResponseHandler *handler, void *callbackData); /** * Sets the ACL for the given bucket or object. * * @param bucketContext gives the bucket and associated parameters for this * request * @param key is the key of the object to set the ACL for; or NULL to set the * ACL for the bucket * @param ownerId is the owner ID of the object/bucket. Unfortunately, S3 * requires this to be valid and thus it must have been fetched by a * previous S3 request, such as a list_buckets request. * @param ownerDisplayName is the owner display name of the object/bucket. * Unfortunately, S3 requires this to be valid and thus it must have * been fetched by a previous S3 request, such as a list_buckets * request. * @param aclGrantCount is the number of ACL grants to set for the * object/bucket * @param aclGrants are the ACL grants to set for the object/bucket * @param requestContext if non-NULL, gives the S3RequestContext to add this * request to, and does not perform the request immediately. If NULL, * performs the request immediately and synchronously. * @param handler gives the callbacks to call as the request is processed and * completed * @param callbackData will be passed in as the callbackData parameter to * all callbacks for this request **/ void S3_set_acl(const S3BucketContext *bucketContext, const char *key, const char *ownerId, const char *ownerDisplayName, int aclGrantCount, const S3AclGrant *aclGrants, S3RequestContext *requestContext, const S3ResponseHandler *handler, void *callbackData); /** ************************************************************************** * Server Access Log Functions ************************************************************************** **/ /** * Gets the service access logging settings for a bucket. The service access * logging settings specify whether or not the S3 service will write service * access logs for requests made for the given bucket, and if so, several * settings controlling how these logs will be written. * * @param bucketContext gives the bucket and associated parameters for this * request; this is the bucket for which service access logging is * being requested * @param targetBucketReturn must be passed in as a buffer of at least * (S3_MAX_BUCKET_NAME_SIZE + 1) bytes in length, and will be filled * in with the target bucket name for access logging for the given * bucket, which is the bucket into which access logs for the specified * bucket will be written. This is returned as an empty string if * service access logging is not enabled for the given bucket. * @param targetPrefixReturn must be passed in as a buffer of at least * (S3_MAX_KEY_SIZE + 1) bytes in length, and will be filled in * with the key prefix for server access logs for the given bucket, * or the empty string if no such prefix is specified. * @param aclGrantCountReturn returns the number of ACL grants that are * associated with the server access logging for the given bucket. * @param aclGrants must be passed in as an array of at least * S3_MAX_ACL_GRANT_COUNT S3AclGrant structures, and these will be * filled in with the target grants associated with the server access * logging for the given bucket, whose number is returned in the * aclGrantCountReturn parameter. These grants will be applied to the * ACL of any server access logging log files generated by the S3 * service for the given bucket. * @param requestContext if non-NULL, gives the S3RequestContext to add this * request to, and does not perform the request immediately. If NULL, * performs the request immediately and synchronously. * @param handler gives the callbacks to call as the request is processed and * completed * @param callbackData will be passed in as the callbackData parameter to * all callbacks for this request **/ void S3_get_server_access_logging(const S3BucketContext *bucketContext, char *targetBucketReturn, char *targetPrefixReturn, int *aclGrantCountReturn, S3AclGrant *aclGrants, S3RequestContext *requestContext, const S3ResponseHandler *handler, void *callbackData); /** * Sets the service access logging settings for a bucket. The service access * logging settings specify whether or not the S3 service will write service * access logs for requests made for the given bucket, and if so, several * settings controlling how these logs will be written. * * @param bucketContext gives the bucket and associated parameters for this * request; this is the bucket for which service access logging is * being set * @param targetBucket gives the target bucket name for access logging for the * given bucket, which is the bucket into which access logs for the * specified bucket will be written. * @param targetPrefix is an option parameter which specifies the key prefix * for server access logs for the given bucket, or NULL if no such * prefix is to be used. * @param aclGrantCount specifies the number of ACL grants that are to be * associated with the server access logging for the given bucket. * @param aclGrants is as an array of S3AclGrant structures, whose number is * given by the aclGrantCount parameter. These grants will be applied * to the ACL of any server access logging log files generated by the * S3 service for the given bucket. * @param requestContext if non-NULL, gives the S3RequestContext to add this * request to, and does not perform the request immediately. If NULL, * performs the request immediately and synchronously. * @param handler gives the callbacks to call as the request is processed and * completed * @param callbackData will be passed in as the callbackData parameter to * all callbacks for this request **/ void S3_set_server_access_logging(const S3BucketContext *bucketContext, const char *targetBucket, const char *targetPrefix, int aclGrantCount, const S3AclGrant *aclGrants, S3RequestContext *requestContext, const S3ResponseHandler *handler, void *callbackData); #ifdef __cplusplus } #endif #endif /* LIBS3_H */ libs3-2.0/inc/request_context.h0000644000175000001440000000241211640553711015232 0ustar bjiusers/** ************************************************************************** * request_context.h * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ #ifndef REQUEST_CONTEXT_H #define REQUEST_CONTEXT_H #include "libs3.h" struct S3RequestContext { CURLM *curlm; struct Request *requests; }; #endif /* REQUEST_CONTEXT_H */ libs3-2.0/inc/mingw/0000755000175000001440000000000011640553711012747 5ustar bjiuserslibs3-2.0/inc/mingw/pthread.h0000644000175000001440000000305111640553711014546 0ustar bjiusers/** ************************************************************************** * pthread.h * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ #ifndef PTHREAD_H #define PTHREAD_H // This is a minimal implementation of pthreads on Windows, implementing just // the APIs needed by libs3 unsigned long pthread_self(); typedef struct { CRITICAL_SECTION criticalSection; } pthread_mutex_t; int pthread_mutex_init(pthread_mutex_t *mutex, void *); int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex); int pthread_mutex_destroy(pthread_mutex_t *mutex); #endif /* PTHREAD_H */ libs3-2.0/inc/mingw/sys/0000755000175000001440000000000011640553711013565 5ustar bjiuserslibs3-2.0/inc/mingw/sys/select.h0000644000175000001440000000232111640553711015213 0ustar bjiusers/** ************************************************************************** * select.h * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ // This file is used only on a MingW build, and converts an include of // sys/select.h to its Windows equivalent #include libs3-2.0/inc/mingw/sys/utsname.h0000644000175000001440000000252111640553711015412 0ustar bjiusers/** ************************************************************************** * utsname.h * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ // This file is used only on a MingW build, and provides an implementation // of POSIX sys/utsname.h #ifndef UTSNAME_H #define UTSNAME_H struct utsname { const char *sysname; const char *machine; }; int uname(struct utsname *); #endif /* UTSNAME_H */ libs3-2.0/inc/error_parser.h0000644000175000001440000000512711640553711014511 0ustar bjiusers/** ************************************************************************** * error_parser.h * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ #ifndef ERROR_PARSER_H #define ERROR_PARSER_H #include "libs3.h" #include "simplexml.h" #include "string_buffer.h" #define EXTRA_DETAILS_SIZE 8 typedef struct ErrorParser { // This is the S3ErrorDetails that this ErrorParser fills in from the // data that it parses S3ErrorDetails s3ErrorDetails; // This is the error XML parser SimpleXml errorXmlParser; // Set to 1 after the first call to add int errorXmlParserInitialized; // Used to buffer the S3 Error Code as it is read in string_buffer(code, 1024); // Used to buffer the S3 Error Message as it is read in string_buffer(message, 1024); // Used to buffer the S3 Error Resource as it is read in string_buffer(resource, 1024); // Used to buffer the S3 Error Further Details as it is read in string_buffer(furtherDetails, 1024); // The extra details; we support up to EXTRA_DETAILS_SIZE of them S3NameValue extraDetails[EXTRA_DETAILS_SIZE]; // This is the buffer from which the names and values used in extraDetails // are allocated string_multibuffer(extraDetailsNamesValues, EXTRA_DETAILS_SIZE * 1024); } ErrorParser; // Always call this void error_parser_initialize(ErrorParser *errorParser); S3Status error_parser_add(ErrorParser *errorParser, char *buffer, int bufferSize); void error_parser_convert_status(ErrorParser *errorParser, S3Status *status); // Always call this void error_parser_deinitialize(ErrorParser *errorParser); #endif /* ERROR_PARSER_H */ libs3-2.0/inc/simplexml.h0000644000175000001440000000444711640553711014022 0ustar bjiusers/** ************************************************************************** * simplexml.h * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ #ifndef SIMPLEXML_H #define SIMPLEXML_H #include "libs3.h" // Simple XML callback. // // elementPath: is the full "path" of the element; i.e. // data would have 'data' in the element // foo/bar/baz. // // Return of anything other than S3StatusOK causes the calling // simplexml_add() function to immediately stop and return the status. // // data is passed in as 0 on end of element typedef S3Status (SimpleXmlCallback)(const char *elementPath, const char *data, int dataLen, void *callbackData); typedef struct SimpleXml { void *xmlParser; SimpleXmlCallback *callback; void *callbackData; char elementPath[512]; int elementPathLen; S3Status status; } SimpleXml; // Simple XML parsing // ---------------------------------------------------------------------------- // Always call this, even if the simplexml doesn't end up being used void simplexml_initialize(SimpleXml *simpleXml, SimpleXmlCallback *callback, void *callbackData); S3Status simplexml_add(SimpleXml *simpleXml, const char *data, int dataLen); // Always call this void simplexml_deinitialize(SimpleXml *simpleXml); #endif /* SIMPLEXML_H */ libs3-2.0/inc/util.h0000644000175000001440000000722011640553711012755 0ustar bjiusers/** ************************************************************************** * util.h * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ #ifndef UTIL_H #define UTIL_H #include #include #include #include "libs3.h" // acl groups #define ACS_URL "http://acs.amazonaws.com/groups/" #define ACS_GROUP_ALL_USERS ACS_URL "global/AllUsers" #define ACS_GROUP_AWS_USERS ACS_URL "global/AuthenticatedUsers" #define ACS_GROUP_LOG_DELIVERY ACS_URL "s3/LogDelivery" // Derived from S3 documentation // This is the maximum number of bytes needed in a "compacted meta header" // buffer, which is a buffer storing all of the compacted meta headers. #define COMPACTED_METADATA_BUFFER_SIZE \ (S3_MAX_METADATA_COUNT * sizeof(S3_METADATA_HEADER_NAME_PREFIX "n: v")) // Maximum url encoded key size; since every single character could require // URL encoding, it's 3 times the size of a key (since each url encoded // character takes 3 characters: %NN) #define MAX_URLENCODED_KEY_SIZE (3 * S3_MAX_KEY_SIZE) // This is the maximum size of a URI that could be passed to S3: // https://s3.amazonaws.com/${BUCKET}/${KEY}?acl // 255 is the maximum bucket length #define MAX_URI_SIZE \ ((sizeof("https:///") - 1) + S3_MAX_HOSTNAME_SIZE + 255 + 1 + \ MAX_URLENCODED_KEY_SIZE + (sizeof("?torrent" - 1)) + 1) // Maximum size of a canonicalized resource #define MAX_CANONICALIZED_RESOURCE_SIZE \ (1 + 255 + 1 + MAX_URLENCODED_KEY_SIZE + (sizeof("?torrent") - 1) + 1) // Utilities ----------------------------------------------------------------- // URL-encodes a string from [src] into [dest]. [dest] must have at least // 3x the number of characters that [source] has. At most [maxSrcSize] bytes // from [src] are encoded; if more are present in [src], 0 is returned from // urlEncode, else nonzero is returned. int urlEncode(char *dest, const char *src, int maxSrcSize); // Returns < 0 on failure >= 0 on success int64_t parseIso8601Time(const char *str); uint64_t parseUnsignedInt(const char *str); // base64 encode bytes. The output buffer must have at least // ((4 * (inLen + 1)) / 3) bytes in it. Returns the number of bytes written // to [out]. int base64Encode(const unsigned char *in, int inLen, char *out); // Compute HMAC-SHA-1 with key [key] and message [message], storing result // in [hmac] void HMAC_SHA1(unsigned char hmac[20], const unsigned char *key, int key_len, const unsigned char *message, int message_len); // Compute a 64-bit hash values given a set of bytes uint64_t hash(const unsigned char *k, int length); // Because Windows seems to be missing isblank(), use our own; it's a very // easy function to write in any case int is_blank(char c); #endif /* UTIL_H */ libs3-2.0/inc/string_buffer.h0000644000175000001440000001141011640553711014633 0ustar bjiusers/** ************************************************************************** * string_buffer.h * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ #ifndef STRING_BUFFER_H #define STRING_BUFFER_H #include // Declare a string_buffer with the given name of the given maximum length #define string_buffer(name, len) \ char name[len + 1]; \ int name##Len // Initialize a string_buffer #define string_buffer_initialize(sb) \ do { \ sb[0] = 0; \ sb##Len = 0; \ } while (0) // Append [len] bytes of [str] to [sb], setting [all_fit] to 1 if it fit, and // 0 if it did not #define string_buffer_append(sb, str, len, all_fit) \ do { \ sb##Len += snprintf(&(sb[sb##Len]), sizeof(sb) - sb##Len - 1, \ "%.*s", (int) (len), str); \ if (sb##Len > (int) (sizeof(sb) - 1)) { \ sb##Len = sizeof(sb) - 1; \ all_fit = 0; \ } \ else { \ all_fit = 1; \ } \ } while (0) // Declare a string multibuffer with the given name of the given maximum size #define string_multibuffer(name, size) \ char name[size]; \ int name##Size // Initialize a string_multibuffer #define string_multibuffer_initialize(smb) \ do { \ smb##Size = 0; \ } while (0) // Evaluates to the current string within the string_multibuffer #define string_multibuffer_current(smb) \ &(smb[smb##Size]) // Adds a new string to the string_multibuffer #define string_multibuffer_add(smb, str, len, all_fit) \ do { \ smb##Size += (snprintf(&(smb[smb##Size]), \ sizeof(smb) - smb##Size, \ "%.*s", (int) (len), str) + 1); \ if (smb##Size > (int) sizeof(smb)) { \ smb##Size = sizeof(smb); \ all_fit = 0; \ } \ else { \ all_fit = 1; \ } \ } while (0) // Appends to the current string in the string_multibuffer. There must be a // current string, meaning that string_multibuffer_add must have been called // at least once for this string_multibuffer. #define string_multibuffer_append(smb, str, len, all_fit) \ do { \ smb##Size--; \ string_multibuffer_add(smb, str, len, all_fit); \ } while (0) #endif /* STRING_BUFFER_H */ libs3-2.0/mswin/0000755000175000001440000000000011640553711012212 5ustar bjiuserslibs3-2.0/mswin/libs3.def0000644000175000001440000000102111640553711013700 0ustar bjiusersEXPORTS S3_convert_acl S3_copy_object S3_create_bucket S3_create_request_context S3_deinitialize S3_delete_bucket S3_delete_object S3_destroy_request_context S3_generate_authenticated_query_string S3_get_acl S3_get_object S3_get_request_context_fdsets S3_get_server_access_logging S3_get_status_name S3_head_object S3_initialize S3_list_bucket S3_list_service S3_put_object S3_runall_request_context S3_runonce_request_context S3_set_acl S3_set_server_access_logging S3_status_is_retryable S3_test_bucket S3_validate_bucket_name libs3-2.0/mswin/rmrf.bat0000644000175000001440000000012511640553711013646 0ustar bjiusers@echo off if exist "%1". ( rmdir /S /Q "%1" ) if exist "%1". ( del /Q "%1" ) libs3-2.0/debian/0000755000175000001440000000000011640553711012277 5ustar bjiuserslibs3-2.0/debian/postinst0000755000175000001440000000002411640553711014104 0ustar bjiusers#!/bin/sh ldconfig libs3-2.0/debian/control.dev0000644000175000001440000000177111640553711014465 0ustar bjiusersPackage: libs3-dev Version: LIBS3_VERSION Architecture: DEBIAN_ARCHITECTURE Section: libdevel Priority: extra Depends: libs3 (>= LIBS3_VERSION) Maintainer: Bryan Ischo Homepage: http://libs3.ischo.com/index.html Description: C Development Library for Amazon S3 Access This library provides an API for using Amazon's S3 service (see http://s3.amazonaws.com). Its design goals are: . - To provide a simple and straightforward API for accessing all of S3's functionality - To not require the developer using libs3 to need to know anything about: - HTTP - XML - SSL In other words, this API is meant to stand on its own, without requiring any implicit knowledge of how S3 services are accessed using HTTP protocols. - To be usable from multithreaded code - To be usable by code which wants to process multiple S3 requests simultaneously from a single thread - To be usable in the simple, straightforward way using sequentialized blocking requests libs3-2.0/debian/changelog.Debian0000644000175000001440000000035211640553711015332 0ustar bjiuserslibs3 (all) unstable; urgency=low * libs3 Debian maintainer and upstream author are identical. Therefore see normal changelog file for Debian changes. -- Bryan Ischo Wed, 06 Aug 2008 09:36:43 -0400 libs3-2.0/debian/control0000644000175000001440000000073611640553711013710 0ustar bjiusersPackage: libs3 Source: THIS LINE WILL BE REMOVED, dpkg-shlibdepends NEEDS IT Version: LIBS3_VERSION Architecture: DEBIAN_ARCHITECTURE Section: net Priority: extra Maintainer: Bryan Ischo Homepage: http://libs3.ischo.com/index.html Description: C Library and Tools for Amazon S3 Access This package includes the libs3 shared object library, needed to run applications compiled against libs3, and additionally contains the s3 utility for accessing Amazon S3. libs3-2.0/debian/changelog0000644000175000001440000000025611640553711014154 0ustar bjiuserslibs3 (all) unstable; urgency=low * This file is not maintained. See project source code for changes. -- Bryan Ischo Wed, 06 Aug 2008 09:36:43 -0400 libs3-2.0/GNUmakefile.osx0000644000175000001440000002367011640553711013747 0ustar bjiusers# GNUmakefile.osx # # Copyright 2008 Bryan Ischo # # This file is part of libs3. # # libs3 is free software: you can redistribute it and/or modify it under the # terms of the GNU General Public License as published by the Free Software # Foundation, version 3 of the License. # # In addition, as a special exception, the copyright holders give # permission to link the code of this library and its programs with the # OpenSSL library, and distribute linked combinations including the two. # # libs3 is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License version 3 # along with libs3, in a file named COPYING. If not, see # . # I tried to use the autoconf/automake/autolocal/etc (i.e. autohell) tools # but I just couldn't stomach them. Since this is a Makefile for POSIX # systems, I will simply do away with autohell completely and use a GNU # Makefile. GNU make ought to be available pretty much everywhere, so I # don't see this being a significant issue for portability. # All commands assume a GNU compiler. For systems which do not use a GNU # compiler, write scripts with the same names as these commands, and taking # the same arguments, and translate the arguments and commands into the # appropriate non-POSIX ones as needed. libs3 assumes a GNU toolchain as # the most portable way to build software possible. Non-POSIX, non-GNU # systems can do the work of supporting this build infrastructure. # -------------------------------------------------------------------------- # Set libs3 version number, unless it is already set. # This is trunk0.trunk0 on the libs3 git master branch; release branches # are created with this set to specific version numbers when releases are # made. LIBS3_VER_MAJOR ?= 2 LIBS3_VER_MINOR ?= 0 LIBS3_VER := $(LIBS3_VER_MAJOR).$(LIBS3_VER_MINOR) # ----------------------------------------------------------------------------- # Determine verbosity. VERBOSE_SHOW should be prepended to every command which # should only be displayed if VERBOSE is set. QUIET_ECHO may be used to # echo text only if VERBOSE is not set. Typically, a VERBOSE_SHOW command will # be paired with a QUIET_ECHO command, to provide a command which is displayed # in VERBOSE mode, along with text which is displayed in non-VERBOSE mode to # describe the command. # # No matter what VERBOSE is defined to, it ends up as true if it's defined. # This will be weird if you defined VERBOSE=false in the environment, and we # switch it to true here; but the meaning of VERBOSE is, "if it's defined to # any value, then verbosity is turned on". So don't define VERBOSE if you # don't want verbosity in the build process. # ----------------------------------------------------------------------------- ifdef VERBOSE VERBOSE = true VERBOSE_ECHO = @ echo VERBOSE_SHOW = QUIET_ECHO = @ echo > /dev/null else VERBOSE = false VERBOSE_ECHO = @ echo > /dev/null VERBOSE_SHOW = @ QUIET_ECHO = @ echo endif # -------------------------------------------------------------------------- # BUILD directory ifndef BUILD ifdef DEBUG BUILD := build-debug else BUILD := build endif endif # -------------------------------------------------------------------------- # DESTDIR directory ifndef DESTDIR DESTDIR := /usr endif # -------------------------------------------------------------------------- # Acquire configuration information for libraries that libs3 depends upon ifndef CURL_LIBS CURL_LIBS := $(shell curl-config --libs) endif ifndef CURL_CFLAGS CURL_CFLAGS := $(shell curl-config --cflags) endif ifndef LIBXML2_LIBS LIBXML2_LIBS := $(shell xml2-config --libs) endif ifndef LIBXML2_CFLAGS LIBXML2_CFLAGS := $(shell xml2-config --cflags) endif # -------------------------------------------------------------------------- # These CFLAGS assume a GNU compiler. For other compilers, write a script # which converts these arguments into their equivalent for that particular # compiler. ifndef CFLAGS ifdef DEBUG CFLAGS := -g else CFLAGS := -O3 endif endif CFLAGS += -Wall -Werror -Wshadow -Wextra -Iinc \ $(CURL_CFLAGS) $(LIBXML2_CFLAGS) \ -DLIBS3_VER_MAJOR=\"$(LIBS3_VER_MAJOR)\" \ -DLIBS3_VER_MINOR=\"$(LIBS3_VER_MINOR)\" \ -DLIBS3_VER=\"$(LIBS3_VER)\" \ -D__STRICT_ANSI__ \ -D_ISOC99_SOURCE \ -fno-common LDFLAGS = $(CURL_LIBS) $(LIBXML2_LIBS) -lpthread # -------------------------------------------------------------------------- # Default targets are everything .PHONY: all all: exported test # -------------------------------------------------------------------------- # Exported targets are the library and driver program .PHONY: exported exported: libs3 s3 headers # -------------------------------------------------------------------------- # Install target .PHONY: install install: exported $(QUIET_ECHO) $(DESTDIR)/bin/s3: Installing executable $(VERBOSE_SHOW) install -ps -m u+rwx,go+rx $(BUILD)/bin/s3 \ $(DESTDIR)/bin/s3 $(QUIET_ECHO) \ $(DESTDIR)/lib/libs3.$(LIBS3_VER).dylib: Installing dynamic library $(VERBOSE_SHOW) install -p -m u+rw,go+r \ $(BUILD)/lib/libs3.$(LIBS3_VER_MAJOR).dylib \ $(DESTDIR)/lib/libs3.$(LIBS3_VER).dylib $(QUIET_ECHO) \ $(DESTDIR)/lib/libs3.$(LIBS3_VER_MAJOR).dylib: Linking dynamic library $(VERBOSE_SHOW) ln -sf libs3.$(LIBS3_VER).dylib \ $(DESTDIR)/lib/libs3.$(LIBS3_VER_MAJOR).dylib $(QUIET_ECHO) $(DESTDIR)/lib/libs3.dylib: Linking dynamic library $(VERBOSE_SHOW) ln -sf libs3.$(LIBS3_VER_MAJOR).dylib \ $(DESTDIR)/lib/libs3.dylib $(QUIET_ECHO) $(DESTDIR)/lib/libs3.a: Installing static library $(VERBOSE_SHOW) install -p -m u+rw,go+r $(BUILD)/lib/libs3.a \ $(DESTDIR)/lib/libs3.a $(QUIET_ECHO) $(DESTDIR)/include/libs3.h: Installing header $(VERBOSE_SHOW) install -p -m u+rw,go+r $(BUILD)/include/libs3.h \ $(DESTDIR)/include/libs3.h # -------------------------------------------------------------------------- # Uninstall target .PHONY: uninstall uninstall: $(QUIET_ECHO) Installed files: Uninstalling $(VERBOSE_SHOW) \ rm -f $(DESTDIR)/bin/s3 \ $(DESTDIR)/lib/libs3.dylib \ $(DESTDIR)/lib/libs3.$(LIBS3_VER_MAJOR).dylib \ $(DESTDIR)/lib/libs3.$(LIBS3_VER).dylib \ $(DESTDIR)/lib/libs3.a \ $(DESTDIR)/include/libs3.h # -------------------------------------------------------------------------- # Compile target patterns $(BUILD)/obj/%.o: src/%.c $(QUIET_ECHO) $@: Compiling object @ mkdir -p $(dir $(BUILD)/dep/$<) @ gcc $(CFLAGS) -M -MG -MQ $@ -DCOMPILINGDEPENDENCIES \ -o $(BUILD)/dep/$(<:%.c=%.d) -c $< @ mkdir -p $(dir $@) @(VERBOSE_SHOW) gcc $(CFLAGS) -o $@ -c $< $(BUILD)/obj/%.do: src/%.c $(QUIET_ECHO) $@: Compiling dynamic object @ mkdir -p $(dir $(BUILD)/dep/$<) @ gcc $(CFLAGS) -M -MG -MQ $@ -DCOMPILINGDEPENDENCIES \ -o $(BUILD)/dep/$(<:%.c=%.dd) -c $< @ mkdir -p $(dir $@) $(VERBOSE_SHOW) gcc $(CFLAGS) -fpic -fPIC -o $@ -c $< # -------------------------------------------------------------------------- # libs3 library targets LIBS3_SHARED = $(BUILD)/lib/libs3.$(LIBS3_VER_MAJOR).dylib LIBS3_STATIC = $(BUILD)/lib/libs3.a .PHONY: libs3 libs3: $(LIBS3_SHARED) $(LIBS3_SHARED_MAJOR) $(BUILD)/lib/libs3.a LIBS3_SOURCES := src/acl.c src/bucket.c src/error_parser.c src/general.c \ src/object.c src/request.c src/request_context.c \ src/response_headers_handler.c src/service_access_logging.c \ src/service.c src/simplexml.c src/util.c $(LIBS3_SHARED): $(LIBS3_SOURCES:src/%.c=$(BUILD)/obj/%.do) $(QUIET_ECHO) $@: Building shared library @ mkdir -p $(dir $@) $(VERBOSE_SHOW) gcc -dynamiclib -install_name \ libs3.$(LIBS3_VER_MAJOR).dylib \ -compatibility_version $(LIBS3_VER_MAJOR) \ -current_version $(LIBS3_VER) -o $@ $^ $(LDFLAGS) $(LIBS3_STATIC): $(LIBS3_SOURCES:src/%.c=$(BUILD)/obj/%.o) $(QUIET_ECHO) $@: Building static library @ mkdir -p $(dir $@) $(VERBOSE_SHOW) $(AR) cr $@ $^ # -------------------------------------------------------------------------- # Driver program targets .PHONY: s3 s3: $(BUILD)/bin/s3 $(BUILD)/bin/s3: $(BUILD)/obj/s3.o $(LIBS3_SHARED) $(QUIET_ECHO) $@: Building executable @ mkdir -p $(dir $@) $(VERBOSE_SHOW) gcc -o $@ $^ $(LDFLAGS) # -------------------------------------------------------------------------- # libs3 header targets .PHONY: headers headers: $(BUILD)/include/libs3.h $(BUILD)/include/libs3.h: inc/libs3.h $(QUIET_ECHO) $@: Linking header @ mkdir -p $(dir $@) $(VERBOSE_SHOW) ln -sf $(abspath $<) $@ # -------------------------------------------------------------------------- # Test targets .PHONY: test test: $(BUILD)/bin/testsimplexml $(BUILD)/bin/testsimplexml: $(BUILD)/obj/testsimplexml.o $(LIBS3_STATIC) $(QUIET_ECHO) $@: Building executable @ mkdir -p $(dir $@) $(VERBOSE_SHOW) gcc -o $@ $^ $(LIBXML2_LIBS) # -------------------------------------------------------------------------- # Clean target .PHONY: clean clean: $(QUIET_ECHO) $(BUILD): Cleaning $(VERBOSE_SHOW) rm -rf $(BUILD) # -------------------------------------------------------------------------- # Clean dependencies target .PHONY: cleandeps cleandeps: $(QUIET_ECHO) $(BUILD)/dep: Cleaning dependencies $(VERBOSE_SHOW) rm -rf $(BUILD)/dep # -------------------------------------------------------------------------- # Dependencies ALL_SOURCES := $(LIBS3_SOURCES) s3.c testsimplexml.c $(foreach i, $(ALL_SOURCES), $(eval -include $(BUILD)/dep/src/$(i:%.c=%.d))) $(foreach i, $(ALL_SOURCES), $(eval -include $(BUILD)/dep/src/$(i:%.c=%.dd))) libs3-2.0/TODO0000644000175000001440000000012711640553711011545 0ustar bjiusers* Implement functions for generating form stuff for posting to s3 * Write s3 man page libs3-2.0/doxyfile0000644000175000001440000010504611640553711012631 0ustar bjiusers# Doxyfile 1.2.14 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # General configuration options #--------------------------------------------------------------------------- # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = libs3 # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = trunk # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = dox # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Brazilian, Chinese, Croatian, Czech, Danish, Dutch, Finnish, French, # German, Greek, Hungarian, Italian, Japanese, Korean, Norwegian, Polish, # Portuguese, Romanian, Russian, Slovak, Slovene, Spanish and Swedish. OUTPUT_LANGUAGE = English # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = YES # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these class will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited # members of a class in the documentation of that class as if those members were # ordinary class members. Constructors, destructors and assignment operators of # the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. It is allowed to use relative paths in the argument list. STRIP_FROM_PATH = # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower case letters. If set to YES upper case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # users are adviced to set this option to NO. CASE_SENSE_NAMES = YES # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like the Qt-style comments (thus requiring an # explict @brief command for a brief description. JAVADOC_AUTOBRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # reimplements. INHERIT_DOCS = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = NO # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consist of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. # For instance some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = inc/libs3.h # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp # *.h++ *.idl *.odl FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories # that are symbolic links (a Unix filesystem feature) are excluded from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. EXCLUDE_PATTERNS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. INPUT_FILTER = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse. FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # If the REFERENCED_BY_RELATION tag is set to YES (the default) # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES (the default) # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the Html help documentation and to the tree view. TOC_EXPAND = NO # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be # generated containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript and frames is required (for instance Mozilla, Netscape 4.0+, # or Internet explorer 4.0+). Note that for large projects the tree generation # can take a very long time. In such cases it is better to disable this feature. # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = YES # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimised for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assigments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_XML = NO #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_PREDEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. PREDEFINED = DOXYGEN # If the MACRO_EXPANSION and EXPAND_PREDEF_ONLY tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line and do not end with a semicolon. Such function macros are typically # used for boiler-plate code, and will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::addtions related to external references #--------------------------------------------------------------------------- # The TAGFILES tag can be used to specify one or more tagfiles. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or # super classes. Setting the tag to NO turns the diagrams off. Note that this # option is superceded by the HAVE_DOT option below. This is only a fallback. It is # recommended to install and use dot, since it yield more powerful graphs. CLASS_DIAGRAMS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = YES # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are gif, jpg, and png # If left blank gif will be used. DOT_IMAGE_FORMAT = gif # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found on the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_WIDTH = 1024 # The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_HEIGHT = 1024 # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermedate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::addtions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO libs3-2.0/src/0000755000175000001440000000000011640553711011644 5ustar bjiuserslibs3-2.0/src/testsimplexml.c0000644000175000001440000000504411640553711014725 0ustar bjiusers/** ************************************************************************** * testsimplexml.c * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ #include #include #include #include #include "simplexml.h" static S3Status simpleXmlCallback(const char *elementPath, const char *data, int dataLen, void *callbackData) { (void) callbackData; printf("[%s]: [%.*s]\n", elementPath, dataLen, data); return S3StatusOK; } // The only argument allowed is a specification of the random seed to use int main(int argc, char **argv) { if (argc > 1) { char *arg = argv[1]; int seed = 0; while (*arg) { seed *= 10; seed += (*arg++ - '0'); } srand(seed); } else { srand(time(0)); } SimpleXml simpleXml; simplexml_initialize(&simpleXml, &simpleXmlCallback, 0); // Read chunks of 10K from stdin, and then feed them in random chunks // to simplexml_add char inbuf[10000]; int amt_read; while ((amt_read = fread(inbuf, 1, sizeof(inbuf), stdin)) > 0) { char *buf = inbuf; while (amt_read) { int amt = (rand() % amt_read) + 1; S3Status status = simplexml_add(&simpleXml, buf, amt); if (status != S3StatusOK) { fprintf(stderr, "ERROR: Parse failure: %d\n", status); simplexml_deinitialize(&simpleXml); return -1; } buf += amt, amt_read -= amt; } } simplexml_deinitialize(&simpleXml); return 0; } libs3-2.0/src/service_access_logging.c0000644000175000001440000005176111640553711016511 0ustar bjiusers/** ************************************************************************** * server_access_logging.c * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ #include #include #include "libs3.h" #include "request.h" // get server access logging--------------------------------------------------- typedef struct ConvertBlsData { char *targetBucketReturn; int targetBucketReturnLen; char *targetPrefixReturn; int targetPrefixReturnLen; int *aclGrantCountReturn; S3AclGrant *aclGrants; string_buffer(emailAddress, S3_MAX_GRANTEE_EMAIL_ADDRESS_SIZE); string_buffer(userId, S3_MAX_GRANTEE_USER_ID_SIZE); string_buffer(userDisplayName, S3_MAX_GRANTEE_DISPLAY_NAME_SIZE); string_buffer(groupUri, 128); string_buffer(permission, 32); } ConvertBlsData; static S3Status convertBlsXmlCallback(const char *elementPath, const char *data, int dataLen, void *callbackData) { ConvertBlsData *caData = (ConvertBlsData *) callbackData; int fit; if (data) { if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/" "TargetBucket")) { caData->targetBucketReturnLen += snprintf(&(caData->targetBucketReturn [caData->targetBucketReturnLen]), 255 - caData->targetBucketReturnLen - 1, "%.*s", dataLen, data); if (caData->targetBucketReturnLen >= 255) { return S3StatusTargetBucketTooLong; } } else if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/" "TargetPrefix")) { caData->targetPrefixReturnLen += snprintf(&(caData->targetPrefixReturn [caData->targetPrefixReturnLen]), 255 - caData->targetPrefixReturnLen - 1, "%.*s", dataLen, data); if (caData->targetPrefixReturnLen >= 255) { return S3StatusTargetPrefixTooLong; } } else if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/" "TargetGrants/Grant/Grantee/EmailAddress")) { // AmazonCustomerByEmail string_buffer_append(caData->emailAddress, data, dataLen, fit); if (!fit) { return S3StatusEmailAddressTooLong; } } else if (!strcmp(elementPath, "AccessControlPolicy/AccessControlList/Grant/" "Grantee/ID")) { // CanonicalUser string_buffer_append(caData->userId, data, dataLen, fit); if (!fit) { return S3StatusUserIdTooLong; } } else if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/" "TargetGrants/Grant/Grantee/DisplayName")) { // CanonicalUser string_buffer_append(caData->userDisplayName, data, dataLen, fit); if (!fit) { return S3StatusUserDisplayNameTooLong; } } else if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/" "TargetGrants/Grant/Grantee/URI")) { // Group string_buffer_append(caData->groupUri, data, dataLen, fit); if (!fit) { return S3StatusGroupUriTooLong; } } else if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/" "TargetGrants/Grant/Permission")) { // Permission string_buffer_append(caData->permission, data, dataLen, fit); if (!fit) { return S3StatusPermissionTooLong; } } } else { if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/" "TargetGrants/Grant")) { // A grant has just been completed; so add the next S3AclGrant // based on the values read if (*(caData->aclGrantCountReturn) == S3_MAX_ACL_GRANT_COUNT) { return S3StatusTooManyGrants; } S3AclGrant *grant = &(caData->aclGrants [*(caData->aclGrantCountReturn)]); if (caData->emailAddress[0]) { grant->granteeType = S3GranteeTypeAmazonCustomerByEmail; strcpy(grant->grantee.amazonCustomerByEmail.emailAddress, caData->emailAddress); } else if (caData->userId[0] && caData->userDisplayName[0]) { grant->granteeType = S3GranteeTypeCanonicalUser; strcpy(grant->grantee.canonicalUser.id, caData->userId); strcpy(grant->grantee.canonicalUser.displayName, caData->userDisplayName); } else if (caData->groupUri[0]) { if (!strcmp(caData->groupUri, ACS_GROUP_AWS_USERS)) { grant->granteeType = S3GranteeTypeAllAwsUsers; } else if (!strcmp(caData->groupUri, ACS_GROUP_ALL_USERS)) { grant->granteeType = S3GranteeTypeAllUsers; } else { return S3StatusBadGrantee; } } else { return S3StatusBadGrantee; } if (!strcmp(caData->permission, "READ")) { grant->permission = S3PermissionRead; } else if (!strcmp(caData->permission, "WRITE")) { grant->permission = S3PermissionWrite; } else if (!strcmp(caData->permission, "READ_ACP")) { grant->permission = S3PermissionReadACP; } else if (!strcmp(caData->permission, "WRITE_ACP")) { grant->permission = S3PermissionWriteACP; } else if (!strcmp(caData->permission, "FULL_CONTROL")) { grant->permission = S3PermissionFullControl; } else { return S3StatusBadPermission; } (*(caData->aclGrantCountReturn))++; string_buffer_initialize(caData->emailAddress); string_buffer_initialize(caData->userId); string_buffer_initialize(caData->userDisplayName); string_buffer_initialize(caData->groupUri); string_buffer_initialize(caData->permission); } } return S3StatusOK; } static S3Status convert_bls(char *blsXml, char *targetBucketReturn, char *targetPrefixReturn, int *aclGrantCountReturn, S3AclGrant *aclGrants) { ConvertBlsData data; data.targetBucketReturn = targetBucketReturn; data.targetBucketReturn[0] = 0; data.targetBucketReturnLen = 0; data.targetPrefixReturn = targetPrefixReturn; data.targetPrefixReturn[0] = 0; data.targetPrefixReturnLen = 0; data.aclGrantCountReturn = aclGrantCountReturn; data.aclGrants = aclGrants; *aclGrantCountReturn = 0; string_buffer_initialize(data.emailAddress); string_buffer_initialize(data.userId); string_buffer_initialize(data.userDisplayName); string_buffer_initialize(data.groupUri); string_buffer_initialize(data.permission); // Use a simplexml parser SimpleXml simpleXml; simplexml_initialize(&simpleXml, &convertBlsXmlCallback, &data); S3Status status = simplexml_add(&simpleXml, blsXml, strlen(blsXml)); simplexml_deinitialize(&simpleXml); return status; } // Use a rather arbitrary max size for the document of 64K #define BLS_XML_DOC_MAXSIZE (64 * 1024) typedef struct GetBlsData { SimpleXml simpleXml; S3ResponsePropertiesCallback *responsePropertiesCallback; S3ResponseCompleteCallback *responseCompleteCallback; void *callbackData; char *targetBucketReturn; char *targetPrefixReturn; int *aclGrantCountReturn; S3AclGrant *aclGrants; string_buffer(blsXmlDocument, BLS_XML_DOC_MAXSIZE); } GetBlsData; static S3Status getBlsPropertiesCallback (const S3ResponseProperties *responseProperties, void *callbackData) { GetBlsData *gsData = (GetBlsData *) callbackData; return (*(gsData->responsePropertiesCallback)) (responseProperties, gsData->callbackData); } static S3Status getBlsDataCallback(int bufferSize, const char *buffer, void *callbackData) { GetBlsData *gsData = (GetBlsData *) callbackData; int fit; string_buffer_append(gsData->blsXmlDocument, buffer, bufferSize, fit); return fit ? S3StatusOK : S3StatusXmlDocumentTooLarge; } static void getBlsCompleteCallback(S3Status requestStatus, const S3ErrorDetails *s3ErrorDetails, void *callbackData) { GetBlsData *gsData = (GetBlsData *) callbackData; if (requestStatus == S3StatusOK) { // Parse the document requestStatus = convert_bls (gsData->blsXmlDocument, gsData->targetBucketReturn, gsData->targetPrefixReturn, gsData->aclGrantCountReturn, gsData->aclGrants); } (*(gsData->responseCompleteCallback)) (requestStatus, s3ErrorDetails, gsData->callbackData); free(gsData); } void S3_get_server_access_logging(const S3BucketContext *bucketContext, char *targetBucketReturn, char *targetPrefixReturn, int *aclGrantCountReturn, S3AclGrant *aclGrants, S3RequestContext *requestContext, const S3ResponseHandler *handler, void *callbackData) { // Create the callback data GetBlsData *gsData = (GetBlsData *) malloc(sizeof(GetBlsData)); if (!gsData) { (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData); return; } gsData->responsePropertiesCallback = handler->propertiesCallback; gsData->responseCompleteCallback = handler->completeCallback; gsData->callbackData = callbackData; gsData->targetBucketReturn = targetBucketReturn; gsData->targetPrefixReturn = targetPrefixReturn; gsData->aclGrantCountReturn = aclGrantCountReturn; gsData->aclGrants = aclGrants; string_buffer_initialize(gsData->blsXmlDocument); *aclGrantCountReturn = 0; // Set up the RequestParams RequestParams params = { HttpRequestTypeGET, // httpRequestType { bucketContext->hostName, // hostName bucketContext->bucketName, // bucketName bucketContext->protocol, // protocol bucketContext->uriStyle, // uriStyle bucketContext->accessKeyId, // accessKeyId bucketContext->secretAccessKey }, // secretAccessKey 0, // key 0, // queryParams "logging", // subResource 0, // copySourceBucketName 0, // copySourceKey 0, // getConditions 0, // startByte 0, // byteCount 0, // putProperties &getBlsPropertiesCallback, // propertiesCallback 0, // toS3Callback 0, // toS3CallbackTotalSize &getBlsDataCallback, // fromS3Callback &getBlsCompleteCallback, // completeCallback gsData // callbackData }; // Perform the request request_perform(¶ms, requestContext); } // set server access logging--------------------------------------------------- static S3Status generateSalXmlDocument(const char *targetBucket, const char *targetPrefix, int aclGrantCount, const S3AclGrant *aclGrants, int *xmlDocumentLenReturn, char *xmlDocument, int xmlDocumentBufferSize) { *xmlDocumentLenReturn = 0; #define append(fmt, ...) \ do { \ *xmlDocumentLenReturn += snprintf \ (&(xmlDocument[*xmlDocumentLenReturn]), \ xmlDocumentBufferSize - *xmlDocumentLenReturn - 1, \ fmt, __VA_ARGS__); \ if (*xmlDocumentLenReturn >= xmlDocumentBufferSize) { \ return S3StatusXmlDocumentTooLarge; \ } \ } while (0) append("%s", ""); if (targetBucket && targetBucket[0]) { append("%s", targetBucket); append("%s", targetPrefix ? targetPrefix : ""); if (aclGrantCount) { append("%s", ""); int i; for (i = 0; i < aclGrantCount; i++) { append("%s", "granteeType) { case S3GranteeTypeAmazonCustomerByEmail: append("AmazonCustomerByEmail\">%s" "", grant->grantee.amazonCustomerByEmail.emailAddress); break; case S3GranteeTypeCanonicalUser: append("CanonicalUser\">%s%s" "", grant->grantee.canonicalUser.id, grant->grantee.canonicalUser.displayName); break; default: // case S3GranteeTypeAllAwsUsers/S3GranteeTypeAllUsers: append("Group\">%s", (grant->granteeType == S3GranteeTypeAllAwsUsers) ? ACS_GROUP_AWS_USERS : ACS_GROUP_ALL_USERS); break; } append("%s", ((grant->permission == S3PermissionRead) ? "READ" : (grant->permission == S3PermissionWrite) ? "WRITE" : (grant->permission == S3PermissionReadACP) ? "READ_ACP" : (grant->permission == S3PermissionWriteACP) ? "WRITE_ACP" : "FULL_CONTROL")); } append("%s", ""); } append("%s", ""); } append("%s", ""); return S3StatusOK; } typedef struct SetSalData { S3ResponsePropertiesCallback *responsePropertiesCallback; S3ResponseCompleteCallback *responseCompleteCallback; void *callbackData; int salXmlDocumentLen; char salXmlDocument[BLS_XML_DOC_MAXSIZE]; int salXmlDocumentBytesWritten; } SetSalData; static S3Status setSalPropertiesCallback (const S3ResponseProperties *responseProperties, void *callbackData) { SetSalData *paData = (SetSalData *) callbackData; return (*(paData->responsePropertiesCallback)) (responseProperties, paData->callbackData); } static int setSalDataCallback(int bufferSize, char *buffer, void *callbackData) { SetSalData *paData = (SetSalData *) callbackData; int remaining = (paData->salXmlDocumentLen - paData->salXmlDocumentBytesWritten); int toCopy = bufferSize > remaining ? remaining : bufferSize; if (!toCopy) { return 0; } memcpy(buffer, &(paData->salXmlDocument [paData->salXmlDocumentBytesWritten]), toCopy); paData->salXmlDocumentBytesWritten += toCopy; return toCopy; } static void setSalCompleteCallback(S3Status requestStatus, const S3ErrorDetails *s3ErrorDetails, void *callbackData) { SetSalData *paData = (SetSalData *) callbackData; (*(paData->responseCompleteCallback)) (requestStatus, s3ErrorDetails, paData->callbackData); free(paData); } void S3_set_server_access_logging(const S3BucketContext *bucketContext, const char *targetBucket, const char *targetPrefix, int aclGrantCount, const S3AclGrant *aclGrants, S3RequestContext *requestContext, const S3ResponseHandler *handler, void *callbackData) { if (aclGrantCount > S3_MAX_ACL_GRANT_COUNT) { (*(handler->completeCallback)) (S3StatusTooManyGrants, 0, callbackData); return; } SetSalData *data = (SetSalData *) malloc(sizeof(SetSalData)); if (!data) { (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData); return; } // Convert aclGrants to XML document S3Status status = generateSalXmlDocument (targetBucket, targetPrefix, aclGrantCount, aclGrants, &(data->salXmlDocumentLen), data->salXmlDocument, sizeof(data->salXmlDocument)); if (status != S3StatusOK) { free(data); (*(handler->completeCallback))(status, 0, callbackData); return; } data->responsePropertiesCallback = handler->propertiesCallback; data->responseCompleteCallback = handler->completeCallback; data->callbackData = callbackData; data->salXmlDocumentBytesWritten = 0; // Set up the RequestParams RequestParams params = { HttpRequestTypePUT, // httpRequestType { bucketContext->hostName, // hostName bucketContext->bucketName, // bucketName bucketContext->protocol, // protocol bucketContext->uriStyle, // uriStyle bucketContext->accessKeyId, // accessKeyId bucketContext->secretAccessKey }, // secretAccessKey 0, // key 0, // queryParams "logging", // subResource 0, // copySourceBucketName 0, // copySourceKey 0, // getConditions 0, // startByte 0, // byteCount 0, // putProperties &setSalPropertiesCallback, // propertiesCallback &setSalDataCallback, // toS3Callback data->salXmlDocumentLen, // toS3CallbackTotalSize 0, // fromS3Callback &setSalCompleteCallback, // completeCallback data // callbackData }; // Perform the request request_perform(¶ms, requestContext); } libs3-2.0/src/simplexml.c0000644000175000001440000001432311640553711014025 0ustar bjiusers/** ************************************************************************** * simplexml.c * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ #include #include #include "simplexml.h" // Use libxml2 for parsing XML. XML is severely overused in modern // computing. It is useful for only a very small subset of tasks, but // software developers who don't know better and are afraid to go against the // grain use it for everything, and in most cases, it is completely // inappropriate. Usually, the document structure is severely under-specified // as well, as is the case with S3. We do our best by just caring about the // most important aspects of the S3 "XML document" responses: the elements and // their values. The SAX API (just about the lamest API ever devised and // proof that XML sucks - well, the real proof is how crappy all of the XML // parsing libraries are, including libxml2 - but I digress) is used here // because we don't need much from the parser and SAX is fast and low memory. // // Note that for simplicity we assume all ASCII here. No attempts are made to // detect non-ASCII sequences in utf-8 and convert them into ASCII in any way. // S3 appears to only use ASCII anyway. static xmlEntityPtr saxGetEntity(void *user_data, const xmlChar *name) { (void) user_data; return xmlGetPredefinedEntity(name); } static void saxStartElement(void *user_data, const xmlChar *nameUtf8, const xmlChar **attr) { (void) attr; SimpleXml *simpleXml = (SimpleXml *) user_data; if (simpleXml->status != S3StatusOK) { return; } // Assume that name has no non-ASCII in it char *name = (char *) nameUtf8; // Append the element to the element path int len = strlen(name); if ((simpleXml->elementPathLen + len + 1) >= (int) sizeof(simpleXml->elementPath)) { // Cannot handle this element, stop! simpleXml->status = S3StatusXmlParseFailure; return; } if (simpleXml->elementPathLen) { simpleXml->elementPath[simpleXml->elementPathLen++] = '/'; } strcpy(&(simpleXml->elementPath[simpleXml->elementPathLen]), name); simpleXml->elementPathLen += len; } static void saxEndElement(void *user_data, const xmlChar *name) { (void) name; SimpleXml *simpleXml = (SimpleXml *) user_data; if (simpleXml->status != S3StatusOK) { return; } // Call back with 0 data simpleXml->status = (*(simpleXml->callback)) (simpleXml->elementPath, 0, 0, simpleXml->callbackData); while ((simpleXml->elementPathLen > 0) && (simpleXml->elementPath[simpleXml->elementPathLen] != '/')) { simpleXml->elementPathLen--; } simpleXml->elementPath[simpleXml->elementPathLen] = 0; } static void saxCharacters(void *user_data, const xmlChar *ch, int len) { SimpleXml *simpleXml = (SimpleXml *) user_data; if (simpleXml->status != S3StatusOK) { return; } simpleXml->status = (*(simpleXml->callback)) (simpleXml->elementPath, (char *) ch, len, simpleXml->callbackData); } static void saxError(void *user_data, const char *msg, ...) { (void) msg; SimpleXml *simpleXml = (SimpleXml *) user_data; if (simpleXml->status != S3StatusOK) { return; } simpleXml->status = S3StatusXmlParseFailure; } static struct _xmlSAXHandler saxHandlerG = { 0, // internalSubsetSAXFunc 0, // isStandaloneSAXFunc 0, // hasInternalSubsetSAXFunc 0, // hasExternalSubsetSAXFunc 0, // resolveEntitySAXFunc &saxGetEntity, // getEntitySAXFunc 0, // entityDeclSAXFunc 0, // notationDeclSAXFunc 0, // attributeDeclSAXFunc 0, // elementDeclSAXFunc 0, // unparsedEntityDeclSAXFunc 0, // setDocumentLocatorSAXFunc 0, // startDocumentSAXFunc 0, // endDocumentSAXFunc &saxStartElement, // startElementSAXFunc &saxEndElement, // endElementSAXFunc 0, // referenceSAXFunc &saxCharacters, // charactersSAXFunc 0, // ignorableWhitespaceSAXFunc 0, // processingInstructionSAXFunc 0, // commentSAXFunc 0, // warningSAXFunc &saxError, // errorSAXFunc &saxError, // fatalErrorSAXFunc 0, // getParameterEntitySAXFunc &saxCharacters, // cdataBlockSAXFunc 0, // externalSubsetSAXFunc 0, // initialized 0, // _private 0, // startElementNsSAX2Func 0, // endElementNsSAX2Func 0 // xmlStructuredErrorFunc serror; }; void simplexml_initialize(SimpleXml *simpleXml, SimpleXmlCallback *callback, void *callbackData) { simpleXml->callback = callback; simpleXml->callbackData = callbackData; simpleXml->elementPathLen = 0; simpleXml->status = S3StatusOK; simpleXml->xmlParser = 0; } void simplexml_deinitialize(SimpleXml *simpleXml) { if (simpleXml->xmlParser) { xmlFreeParserCtxt(simpleXml->xmlParser); } } S3Status simplexml_add(SimpleXml *simpleXml, const char *data, int dataLen) { if (!simpleXml->xmlParser && (!(simpleXml->xmlParser = xmlCreatePushParserCtxt (&saxHandlerG, simpleXml, 0, 0, 0)))) { return S3StatusInternalError; } if (xmlParseChunk((xmlParserCtxtPtr) simpleXml->xmlParser, data, dataLen, 0)) { return S3StatusXmlParseFailure; } return simpleXml->status; } libs3-2.0/src/object.c0000644000175000001440000003545011640553711013265 0ustar bjiusers/** ************************************************************************** * object.c * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ #include #include #include "libs3.h" #include "request.h" // put object ---------------------------------------------------------------- void S3_put_object(const S3BucketContext *bucketContext, const char *key, uint64_t contentLength, const S3PutProperties *putProperties, S3RequestContext *requestContext, const S3PutObjectHandler *handler, void *callbackData) { // Set up the RequestParams RequestParams params = { HttpRequestTypePUT, // httpRequestType { bucketContext->hostName, // hostName bucketContext->bucketName, // bucketName bucketContext->protocol, // protocol bucketContext->uriStyle, // uriStyle bucketContext->accessKeyId, // accessKeyId bucketContext->secretAccessKey }, // secretAccessKey key, // key 0, // queryParams 0, // subResource 0, // copySourceBucketName 0, // copySourceKey 0, // getConditions 0, // startByte 0, // byteCount putProperties, // putProperties handler->responseHandler.propertiesCallback, // propertiesCallback handler->putObjectDataCallback, // toS3Callback contentLength, // toS3CallbackTotalSize 0, // fromS3Callback handler->responseHandler.completeCallback, // completeCallback callbackData // callbackData }; // Perform the request request_perform(¶ms, requestContext); } // copy object --------------------------------------------------------------- typedef struct CopyObjectData { SimpleXml simpleXml; S3ResponsePropertiesCallback *responsePropertiesCallback; S3ResponseCompleteCallback *responseCompleteCallback; void *callbackData; int64_t *lastModifiedReturn; int eTagReturnSize; char *eTagReturn; int eTagReturnLen; string_buffer(lastModified, 256); } CopyObjectData; static S3Status copyObjectXmlCallback(const char *elementPath, const char *data, int dataLen, void *callbackData) { CopyObjectData *coData = (CopyObjectData *) callbackData; int fit; if (data) { if (!strcmp(elementPath, "CopyObjectResult/LastModified")) { string_buffer_append(coData->lastModified, data, dataLen, fit); } else if (!strcmp(elementPath, "CopyObjectResult/ETag")) { if (coData->eTagReturnSize && coData->eTagReturn) { coData->eTagReturnLen += snprintf(&(coData->eTagReturn[coData->eTagReturnLen]), coData->eTagReturnSize - coData->eTagReturnLen - 1, "%.*s", dataLen, data); if (coData->eTagReturnLen >= coData->eTagReturnSize) { return S3StatusXmlParseFailure; } } } } /* Avoid compiler error about variable set but not used */ (void) fit; return S3StatusOK; } static S3Status copyObjectPropertiesCallback (const S3ResponseProperties *responseProperties, void *callbackData) { CopyObjectData *coData = (CopyObjectData *) callbackData; return (*(coData->responsePropertiesCallback)) (responseProperties, coData->callbackData); } static S3Status copyObjectDataCallback(int bufferSize, const char *buffer, void *callbackData) { CopyObjectData *coData = (CopyObjectData *) callbackData; return simplexml_add(&(coData->simpleXml), buffer, bufferSize); } static void copyObjectCompleteCallback(S3Status requestStatus, const S3ErrorDetails *s3ErrorDetails, void *callbackData) { CopyObjectData *coData = (CopyObjectData *) callbackData; if (coData->lastModifiedReturn) { time_t lastModified = -1; if (coData->lastModifiedLen) { lastModified = parseIso8601Time(coData->lastModified); } *(coData->lastModifiedReturn) = lastModified; } (*(coData->responseCompleteCallback)) (requestStatus, s3ErrorDetails, coData->callbackData); simplexml_deinitialize(&(coData->simpleXml)); free(coData); } void S3_copy_object(const S3BucketContext *bucketContext, const char *key, const char *destinationBucket, const char *destinationKey, const S3PutProperties *putProperties, int64_t *lastModifiedReturn, int eTagReturnSize, char *eTagReturn, S3RequestContext *requestContext, const S3ResponseHandler *handler, void *callbackData) { // Create the callback data CopyObjectData *data = (CopyObjectData *) malloc(sizeof(CopyObjectData)); if (!data) { (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData); return; } simplexml_initialize(&(data->simpleXml), ©ObjectXmlCallback, data); data->responsePropertiesCallback = handler->propertiesCallback; data->responseCompleteCallback = handler->completeCallback; data->callbackData = callbackData; data->lastModifiedReturn = lastModifiedReturn; data->eTagReturnSize = eTagReturnSize; data->eTagReturn = eTagReturn; if (data->eTagReturnSize && data->eTagReturn) { data->eTagReturn[0] = 0; } data->eTagReturnLen = 0; string_buffer_initialize(data->lastModified); // Set up the RequestParams RequestParams params = { HttpRequestTypeCOPY, // httpRequestType { bucketContext->hostName, // hostName destinationBucket ? destinationBucket : bucketContext->bucketName, // bucketName bucketContext->protocol, // protocol bucketContext->uriStyle, // uriStyle bucketContext->accessKeyId, // accessKeyId bucketContext->secretAccessKey }, // secretAccessKey destinationKey ? destinationKey : key, // key 0, // queryParams 0, // subResource bucketContext->bucketName, // copySourceBucketName key, // copySourceKey 0, // getConditions 0, // startByte 0, // byteCount putProperties, // putProperties ©ObjectPropertiesCallback, // propertiesCallback 0, // toS3Callback 0, // toS3CallbackTotalSize ©ObjectDataCallback, // fromS3Callback ©ObjectCompleteCallback, // completeCallback data // callbackData }; // Perform the request request_perform(¶ms, requestContext); } // get object ---------------------------------------------------------------- void S3_get_object(const S3BucketContext *bucketContext, const char *key, const S3GetConditions *getConditions, uint64_t startByte, uint64_t byteCount, S3RequestContext *requestContext, const S3GetObjectHandler *handler, void *callbackData) { // Set up the RequestParams RequestParams params = { HttpRequestTypeGET, // httpRequestType { bucketContext->hostName, // hostName bucketContext->bucketName, // bucketName bucketContext->protocol, // protocol bucketContext->uriStyle, // uriStyle bucketContext->accessKeyId, // accessKeyId bucketContext->secretAccessKey }, // secretAccessKey key, // key 0, // queryParams 0, // subResource 0, // copySourceBucketName 0, // copySourceKey getConditions, // getConditions startByte, // startByte byteCount, // byteCount 0, // putProperties handler->responseHandler.propertiesCallback, // propertiesCallback 0, // toS3Callback 0, // toS3CallbackTotalSize handler->getObjectDataCallback, // fromS3Callback handler->responseHandler.completeCallback, // completeCallback callbackData // callbackData }; // Perform the request request_perform(¶ms, requestContext); } // head object --------------------------------------------------------------- void S3_head_object(const S3BucketContext *bucketContext, const char *key, S3RequestContext *requestContext, const S3ResponseHandler *handler, void *callbackData) { // Set up the RequestParams RequestParams params = { HttpRequestTypeHEAD, // httpRequestType { bucketContext->hostName, // hostName bucketContext->bucketName, // bucketName bucketContext->protocol, // protocol bucketContext->uriStyle, // uriStyle bucketContext->accessKeyId, // accessKeyId bucketContext->secretAccessKey }, // secretAccessKey key, // key 0, // queryParams 0, // subResource 0, // copySourceBucketName 0, // copySourceKey 0, // getConditions 0, // startByte 0, // byteCount 0, // putProperties handler->propertiesCallback, // propertiesCallback 0, // toS3Callback 0, // toS3CallbackTotalSize 0, // fromS3Callback handler->completeCallback, // completeCallback callbackData // callbackData }; // Perform the request request_perform(¶ms, requestContext); } // delete object -------------------------------------------------------------- void S3_delete_object(const S3BucketContext *bucketContext, const char *key, S3RequestContext *requestContext, const S3ResponseHandler *handler, void *callbackData) { // Set up the RequestParams RequestParams params = { HttpRequestTypeDELETE, // httpRequestType { bucketContext->hostName, // hostName bucketContext->bucketName, // bucketName bucketContext->protocol, // protocol bucketContext->uriStyle, // uriStyle bucketContext->accessKeyId, // accessKeyId bucketContext->secretAccessKey }, // secretAccessKey key, // key 0, // queryParams 0, // subResource 0, // copySourceBucketName 0, // copySourceKey 0, // getConditions 0, // startByte 0, // byteCount 0, // putProperties handler->propertiesCallback, // propertiesCallback 0, // toS3Callback 0, // toS3CallbackTotalSize 0, // fromS3Callback handler->completeCallback, // completeCallback callbackData // callbackData }; // Perform the request request_perform(¶ms, requestContext); } libs3-2.0/src/general.c0000644000175000001440000004134111640553711013430 0ustar bjiusers/** ************************************************************************** * general.c * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ #include #include #include "request.h" #include "simplexml.h" #include "util.h" static int initializeCountG = 0; S3Status S3_initialize(const char *userAgentInfo, int flags, const char *defaultS3HostName) { if (initializeCountG++) { return S3StatusOK; } return request_api_initialize(userAgentInfo, flags, defaultS3HostName); } void S3_deinitialize() { if (--initializeCountG) { return; } request_api_deinitialize(); } const char *S3_get_status_name(S3Status status) { switch (status) { #define handlecase(s) \ case S3Status##s: \ return #s handlecase(OK); handlecase(InternalError); handlecase(OutOfMemory); handlecase(Interrupted); handlecase(InvalidBucketNameTooLong); handlecase(InvalidBucketNameFirstCharacter); handlecase(InvalidBucketNameCharacter); handlecase(InvalidBucketNameCharacterSequence); handlecase(InvalidBucketNameTooShort); handlecase(InvalidBucketNameDotQuadNotation); handlecase(QueryParamsTooLong); handlecase(FailedToInitializeRequest); handlecase(MetaDataHeadersTooLong); handlecase(BadMetaData); handlecase(BadContentType); handlecase(ContentTypeTooLong); handlecase(BadMD5); handlecase(MD5TooLong); handlecase(BadCacheControl); handlecase(CacheControlTooLong); handlecase(BadContentDispositionFilename); handlecase(ContentDispositionFilenameTooLong); handlecase(BadContentEncoding); handlecase(ContentEncodingTooLong); handlecase(BadIfMatchETag); handlecase(IfMatchETagTooLong); handlecase(BadIfNotMatchETag); handlecase(IfNotMatchETagTooLong); handlecase(HeadersTooLong); handlecase(KeyTooLong); handlecase(UriTooLong); handlecase(XmlParseFailure); handlecase(EmailAddressTooLong); handlecase(UserIdTooLong); handlecase(UserDisplayNameTooLong); handlecase(GroupUriTooLong); handlecase(PermissionTooLong); handlecase(TargetBucketTooLong); handlecase(TargetPrefixTooLong); handlecase(TooManyGrants); handlecase(BadGrantee); handlecase(BadPermission); handlecase(XmlDocumentTooLarge); handlecase(NameLookupError); handlecase(FailedToConnect); handlecase(ServerFailedVerification); handlecase(ConnectionFailed); handlecase(AbortedByCallback); handlecase(ErrorAccessDenied); handlecase(ErrorAccountProblem); handlecase(ErrorAmbiguousGrantByEmailAddress); handlecase(ErrorBadDigest); handlecase(ErrorBucketAlreadyExists); handlecase(ErrorBucketAlreadyOwnedByYou); handlecase(ErrorBucketNotEmpty); handlecase(ErrorCredentialsNotSupported); handlecase(ErrorCrossLocationLoggingProhibited); handlecase(ErrorEntityTooSmall); handlecase(ErrorEntityTooLarge); handlecase(ErrorExpiredToken); handlecase(ErrorIncompleteBody); handlecase(ErrorIncorrectNumberOfFilesInPostRequest); handlecase(ErrorInlineDataTooLarge); handlecase(ErrorInternalError); handlecase(ErrorInvalidAccessKeyId); handlecase(ErrorInvalidAddressingHeader); handlecase(ErrorInvalidArgument); handlecase(ErrorInvalidBucketName); handlecase(ErrorInvalidDigest); handlecase(ErrorInvalidLocationConstraint); handlecase(ErrorInvalidPayer); handlecase(ErrorInvalidPolicyDocument); handlecase(ErrorInvalidRange); handlecase(ErrorInvalidSecurity); handlecase(ErrorInvalidSOAPRequest); handlecase(ErrorInvalidStorageClass); handlecase(ErrorInvalidTargetBucketForLogging); handlecase(ErrorInvalidToken); handlecase(ErrorInvalidURI); handlecase(ErrorKeyTooLong); handlecase(ErrorMalformedACLError); handlecase(ErrorMalformedXML); handlecase(ErrorMaxMessageLengthExceeded); handlecase(ErrorMaxPostPreDataLengthExceededError); handlecase(ErrorMetadataTooLarge); handlecase(ErrorMethodNotAllowed); handlecase(ErrorMissingAttachment); handlecase(ErrorMissingContentLength); handlecase(ErrorMissingSecurityElement); handlecase(ErrorMissingSecurityHeader); handlecase(ErrorNoLoggingStatusForKey); handlecase(ErrorNoSuchBucket); handlecase(ErrorNoSuchKey); handlecase(ErrorNotImplemented); handlecase(ErrorNotSignedUp); handlecase(ErrorOperationAborted); handlecase(ErrorPermanentRedirect); handlecase(ErrorPreconditionFailed); handlecase(ErrorRedirect); handlecase(ErrorRequestIsNotMultiPartContent); handlecase(ErrorRequestTimeout); handlecase(ErrorRequestTimeTooSkewed); handlecase(ErrorRequestTorrentOfBucketError); handlecase(ErrorSignatureDoesNotMatch); handlecase(ErrorSlowDown); handlecase(ErrorTemporaryRedirect); handlecase(ErrorTokenRefreshRequired); handlecase(ErrorTooManyBuckets); handlecase(ErrorUnexpectedContent); handlecase(ErrorUnresolvableGrantByEmailAddress); handlecase(ErrorUserKeyMustBeSpecified); handlecase(ErrorUnknown); handlecase(HttpErrorMovedTemporarily); handlecase(HttpErrorBadRequest); handlecase(HttpErrorForbidden); handlecase(HttpErrorNotFound); handlecase(HttpErrorConflict); handlecase(HttpErrorUnknown); } return "Unknown"; } S3Status S3_validate_bucket_name(const char *bucketName, S3UriStyle uriStyle) { int virtualHostStyle = (uriStyle == S3UriStyleVirtualHost); int len = 0, maxlen = virtualHostStyle ? 63 : 255; const char *b = bucketName; int hasDot = 0; int hasNonDigit = 0; while (*b) { if (len == maxlen) { return S3StatusInvalidBucketNameTooLong; } else if (isalpha(*b)) { len++, b++; hasNonDigit = 1; } else if (isdigit(*b)) { len++, b++; } else if (len == 0) { return S3StatusInvalidBucketNameFirstCharacter; } else if (*b == '_') { /* Virtual host style bucket names cannot have underscores */ if (virtualHostStyle) { return S3StatusInvalidBucketNameCharacter; } len++, b++; hasNonDigit = 1; } else if (*b == '-') { /* Virtual host style bucket names cannot have .- */ if (virtualHostStyle && (b > bucketName) && (*(b - 1) == '.')) { return S3StatusInvalidBucketNameCharacterSequence; } len++, b++; hasNonDigit = 1; } else if (*b == '.') { /* Virtual host style bucket names cannot have -. */ if (virtualHostStyle && (b > bucketName) && (*(b - 1) == '-')) { return S3StatusInvalidBucketNameCharacterSequence; } len++, b++; hasDot = 1; } else { return S3StatusInvalidBucketNameCharacter; } } if (len < 3) { return S3StatusInvalidBucketNameTooShort; } /* It's not clear from Amazon's documentation exactly what 'IP address style' means. In its strictest sense, it could mean 'could be a valid IP address', which would mean that 255.255.255.255 would be invalid, wherase 256.256.256.256 would be valid. Or it could mean 'has 4 sets of digits separated by dots'. Who knows. Let's just be really conservative here: if it has any dots, and no non-digit characters, then we reject it */ if (hasDot && !hasNonDigit) { return S3StatusInvalidBucketNameDotQuadNotation; } return S3StatusOK; } typedef struct ConvertAclData { char *ownerId; int ownerIdLen; char *ownerDisplayName; int ownerDisplayNameLen; int *aclGrantCountReturn; S3AclGrant *aclGrants; string_buffer(emailAddress, S3_MAX_GRANTEE_EMAIL_ADDRESS_SIZE); string_buffer(userId, S3_MAX_GRANTEE_USER_ID_SIZE); string_buffer(userDisplayName, S3_MAX_GRANTEE_DISPLAY_NAME_SIZE); string_buffer(groupUri, 128); string_buffer(permission, 32); } ConvertAclData; static S3Status convertAclXmlCallback(const char *elementPath, const char *data, int dataLen, void *callbackData) { ConvertAclData *caData = (ConvertAclData *) callbackData; int fit; if (data) { if (!strcmp(elementPath, "AccessControlPolicy/Owner/ID")) { caData->ownerIdLen += snprintf(&(caData->ownerId[caData->ownerIdLen]), S3_MAX_GRANTEE_USER_ID_SIZE - caData->ownerIdLen - 1, "%.*s", dataLen, data); if (caData->ownerIdLen >= S3_MAX_GRANTEE_USER_ID_SIZE) { return S3StatusUserIdTooLong; } } else if (!strcmp(elementPath, "AccessControlPolicy/Owner/" "DisplayName")) { caData->ownerDisplayNameLen += snprintf(&(caData->ownerDisplayName [caData->ownerDisplayNameLen]), S3_MAX_GRANTEE_DISPLAY_NAME_SIZE - caData->ownerDisplayNameLen - 1, "%.*s", dataLen, data); if (caData->ownerDisplayNameLen >= S3_MAX_GRANTEE_DISPLAY_NAME_SIZE) { return S3StatusUserDisplayNameTooLong; } } else if (!strcmp(elementPath, "AccessControlPolicy/AccessControlList/Grant/" "Grantee/EmailAddress")) { // AmazonCustomerByEmail string_buffer_append(caData->emailAddress, data, dataLen, fit); if (!fit) { return S3StatusEmailAddressTooLong; } } else if (!strcmp(elementPath, "AccessControlPolicy/AccessControlList/Grant/" "Grantee/ID")) { // CanonicalUser string_buffer_append(caData->userId, data, dataLen, fit); if (!fit) { return S3StatusUserIdTooLong; } } else if (!strcmp(elementPath, "AccessControlPolicy/AccessControlList/Grant/" "Grantee/DisplayName")) { // CanonicalUser string_buffer_append(caData->userDisplayName, data, dataLen, fit); if (!fit) { return S3StatusUserDisplayNameTooLong; } } else if (!strcmp(elementPath, "AccessControlPolicy/AccessControlList/Grant/" "Grantee/URI")) { // Group string_buffer_append(caData->groupUri, data, dataLen, fit); if (!fit) { return S3StatusGroupUriTooLong; } } else if (!strcmp(elementPath, "AccessControlPolicy/AccessControlList/Grant/" "Permission")) { // Permission string_buffer_append(caData->permission, data, dataLen, fit); if (!fit) { return S3StatusPermissionTooLong; } } } else { if (!strcmp(elementPath, "AccessControlPolicy/AccessControlList/" "Grant")) { // A grant has just been completed; so add the next S3AclGrant // based on the values read if (*(caData->aclGrantCountReturn) == S3_MAX_ACL_GRANT_COUNT) { return S3StatusTooManyGrants; } S3AclGrant *grant = &(caData->aclGrants [*(caData->aclGrantCountReturn)]); if (caData->emailAddress[0]) { grant->granteeType = S3GranteeTypeAmazonCustomerByEmail; strcpy(grant->grantee.amazonCustomerByEmail.emailAddress, caData->emailAddress); } else if (caData->userId[0] && caData->userDisplayName[0]) { grant->granteeType = S3GranteeTypeCanonicalUser; strcpy(grant->grantee.canonicalUser.id, caData->userId); strcpy(grant->grantee.canonicalUser.displayName, caData->userDisplayName); } else if (caData->groupUri[0]) { if (!strcmp(caData->groupUri, ACS_GROUP_AWS_USERS)) { grant->granteeType = S3GranteeTypeAllAwsUsers; } else if (!strcmp(caData->groupUri, ACS_GROUP_ALL_USERS)) { grant->granteeType = S3GranteeTypeAllUsers; } else if (!strcmp(caData->groupUri, ACS_GROUP_LOG_DELIVERY)) { grant->granteeType = S3GranteeTypeLogDelivery; } else { return S3StatusBadGrantee; } } else { return S3StatusBadGrantee; } if (!strcmp(caData->permission, "READ")) { grant->permission = S3PermissionRead; } else if (!strcmp(caData->permission, "WRITE")) { grant->permission = S3PermissionWrite; } else if (!strcmp(caData->permission, "READ_ACP")) { grant->permission = S3PermissionReadACP; } else if (!strcmp(caData->permission, "WRITE_ACP")) { grant->permission = S3PermissionWriteACP; } else if (!strcmp(caData->permission, "FULL_CONTROL")) { grant->permission = S3PermissionFullControl; } else { return S3StatusBadPermission; } (*(caData->aclGrantCountReturn))++; string_buffer_initialize(caData->emailAddress); string_buffer_initialize(caData->userId); string_buffer_initialize(caData->userDisplayName); string_buffer_initialize(caData->groupUri); string_buffer_initialize(caData->permission); } } return S3StatusOK; } S3Status S3_convert_acl(char *aclXml, char *ownerId, char *ownerDisplayName, int *aclGrantCountReturn, S3AclGrant *aclGrants) { ConvertAclData data; data.ownerId = ownerId; data.ownerIdLen = 0; data.ownerId[0] = 0; data.ownerDisplayName = ownerDisplayName; data.ownerDisplayNameLen = 0; data.ownerDisplayName[0] = 0; data.aclGrantCountReturn = aclGrantCountReturn; data.aclGrants = aclGrants; *aclGrantCountReturn = 0; string_buffer_initialize(data.emailAddress); string_buffer_initialize(data.userId); string_buffer_initialize(data.userDisplayName); string_buffer_initialize(data.groupUri); string_buffer_initialize(data.permission); // Use a simplexml parser SimpleXml simpleXml; simplexml_initialize(&simpleXml, &convertAclXmlCallback, &data); S3Status status = simplexml_add(&simpleXml, aclXml, strlen(aclXml)); simplexml_deinitialize(&simpleXml); return status; } int S3_status_is_retryable(S3Status status) { switch (status) { case S3StatusNameLookupError: case S3StatusFailedToConnect: case S3StatusConnectionFailed: case S3StatusErrorInternalError: case S3StatusErrorOperationAborted: case S3StatusErrorRequestTimeout: return 1; default: return 0; } } libs3-2.0/src/request_context.c0000644000175000001440000001471711640553711015256 0ustar bjiusers/** ************************************************************************** * request_context.c * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ #include #include #include #include "request.h" #include "request_context.h" S3Status S3_create_request_context(S3RequestContext **requestContextReturn) { *requestContextReturn = (S3RequestContext *) malloc(sizeof(S3RequestContext)); if (!*requestContextReturn) { return S3StatusOutOfMemory; } if (!((*requestContextReturn)->curlm = curl_multi_init())) { free(*requestContextReturn); return S3StatusOutOfMemory; } (*requestContextReturn)->requests = 0; return S3StatusOK; } void S3_destroy_request_context(S3RequestContext *requestContext) { curl_multi_cleanup(requestContext->curlm); // For each request in the context, call back its done method with // 'interrupted' status Request *r = requestContext->requests, *rFirst = r; if (r) do { r->status = S3StatusInterrupted; Request *rNext = r->next; request_finish(r); r = rNext; } while (r != rFirst); free(requestContext); } S3Status S3_runall_request_context(S3RequestContext *requestContext) { int requestsRemaining; do { fd_set readfds, writefds, exceptfds; FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds); int maxfd; S3Status status = S3_get_request_context_fdsets (requestContext, &readfds, &writefds, &exceptfds, &maxfd); if (status != S3StatusOK) { return status; } // curl will return -1 if it hasn't even created any fds yet because // none of the connections have started yet. In this case, don't // do the select at all, because it will wait forever; instead, just // skip it and go straight to running the underlying CURL handles if (maxfd != -1) { int64_t timeout = S3_get_request_context_timeout(requestContext); struct timeval tv = { timeout / 1000, (timeout % 1000) * 1000 }; select(maxfd + 1, &readfds, &writefds, &exceptfds, (timeout == -1) ? 0 : &tv); } status = S3_runonce_request_context(requestContext, &requestsRemaining); if (status != S3StatusOK) { return status; } } while (requestsRemaining); return S3StatusOK; } S3Status S3_runonce_request_context(S3RequestContext *requestContext, int *requestsRemainingReturn) { CURLMcode status; do { status = curl_multi_perform(requestContext->curlm, requestsRemainingReturn); switch (status) { case CURLM_OK: case CURLM_CALL_MULTI_PERFORM: break; case CURLM_OUT_OF_MEMORY: return S3StatusOutOfMemory; default: return S3StatusInternalError; } CURLMsg *msg; int junk; while ((msg = curl_multi_info_read(requestContext->curlm, &junk))) { if (msg->msg != CURLMSG_DONE) { return S3StatusInternalError; } Request *request; if (curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, (char **) (char *) &request) != CURLE_OK) { return S3StatusInternalError; } // Remove the request from the list of requests if (request->prev == request->next) { // It was the only one on the list requestContext->requests = 0; } else { // It doesn't matter what the order of them are, so just in // case request was at the head of the list, put the one after // request to the head of the list requestContext->requests = request->next; request->prev->next = request->next; request->next->prev = request->prev; } if ((msg->data.result != CURLE_OK) && (request->status == S3StatusOK)) { request->status = request_curl_code_to_status (msg->data.result); } if (curl_multi_remove_handle(requestContext->curlm, msg->easy_handle) != CURLM_OK) { return S3StatusInternalError; } // Finish the request, ensuring that all callbacks have been made, // and also releases the request request_finish(request); // Now, since a callback was made, there may be new requests // queued up to be performed immediately, so do so status = CURLM_CALL_MULTI_PERFORM; } } while (status == CURLM_CALL_MULTI_PERFORM); return S3StatusOK; } S3Status S3_get_request_context_fdsets(S3RequestContext *requestContext, fd_set *readFdSet, fd_set *writeFdSet, fd_set *exceptFdSet, int *maxFd) { return ((curl_multi_fdset(requestContext->curlm, readFdSet, writeFdSet, exceptFdSet, maxFd) == CURLM_OK) ? S3StatusOK : S3StatusInternalError); } int64_t S3_get_request_context_timeout(S3RequestContext *requestContext) { long timeout; if (curl_multi_timeout(requestContext->curlm, &timeout) != CURLM_OK) { timeout = 0; } return timeout; } libs3-2.0/src/acl.c0000644000175000001440000003162411640553711012555 0ustar bjiusers/** ************************************************************************** * acl.c * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ #include #include #include "libs3.h" #include "request.h" // Use a rather arbitrary max size for the document of 64K #define ACL_XML_DOC_MAXSIZE (64 * 1024) // get acl ------------------------------------------------------------------- typedef struct GetAclData { SimpleXml simpleXml; S3ResponsePropertiesCallback *responsePropertiesCallback; S3ResponseCompleteCallback *responseCompleteCallback; void *callbackData; int *aclGrantCountReturn; S3AclGrant *aclGrants; char *ownerId; char *ownerDisplayName; string_buffer(aclXmlDocument, ACL_XML_DOC_MAXSIZE); } GetAclData; static S3Status getAclPropertiesCallback (const S3ResponseProperties *responseProperties, void *callbackData) { GetAclData *gaData = (GetAclData *) callbackData; return (*(gaData->responsePropertiesCallback)) (responseProperties, gaData->callbackData); } static S3Status getAclDataCallback(int bufferSize, const char *buffer, void *callbackData) { GetAclData *gaData = (GetAclData *) callbackData; int fit; string_buffer_append(gaData->aclXmlDocument, buffer, bufferSize, fit); return fit ? S3StatusOK : S3StatusXmlDocumentTooLarge; } static void getAclCompleteCallback(S3Status requestStatus, const S3ErrorDetails *s3ErrorDetails, void *callbackData) { GetAclData *gaData = (GetAclData *) callbackData; if (requestStatus == S3StatusOK) { // Parse the document requestStatus = S3_convert_acl (gaData->aclXmlDocument, gaData->ownerId, gaData->ownerDisplayName, gaData->aclGrantCountReturn, gaData->aclGrants); } (*(gaData->responseCompleteCallback)) (requestStatus, s3ErrorDetails, gaData->callbackData); free(gaData); } void S3_get_acl(const S3BucketContext *bucketContext, const char *key, char *ownerId, char *ownerDisplayName, int *aclGrantCountReturn, S3AclGrant *aclGrants, S3RequestContext *requestContext, const S3ResponseHandler *handler, void *callbackData) { // Create the callback data GetAclData *gaData = (GetAclData *) malloc(sizeof(GetAclData)); if (!gaData) { (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData); return; } gaData->responsePropertiesCallback = handler->propertiesCallback; gaData->responseCompleteCallback = handler->completeCallback; gaData->callbackData = callbackData; gaData->aclGrantCountReturn = aclGrantCountReturn; gaData->aclGrants = aclGrants; gaData->ownerId = ownerId; gaData->ownerDisplayName = ownerDisplayName; string_buffer_initialize(gaData->aclXmlDocument); *aclGrantCountReturn = 0; // Set up the RequestParams RequestParams params = { HttpRequestTypeGET, // httpRequestType { bucketContext->hostName, // hostName bucketContext->bucketName, // bucketName bucketContext->protocol, // protocol bucketContext->uriStyle, // uriStyle bucketContext->accessKeyId, // accessKeyId bucketContext->secretAccessKey }, // secretAccessKey key, // key 0, // queryParams "acl", // subResource 0, // copySourceBucketName 0, // copySourceKey 0, // getConditions 0, // startByte 0, // byteCount 0, // putProperties &getAclPropertiesCallback, // propertiesCallback 0, // toS3Callback 0, // toS3CallbackTotalSize &getAclDataCallback, // fromS3Callback &getAclCompleteCallback, // completeCallback gaData // callbackData }; // Perform the request request_perform(¶ms, requestContext); } // set acl ------------------------------------------------------------------- static S3Status generateAclXmlDocument(const char *ownerId, const char *ownerDisplayName, int aclGrantCount, const S3AclGrant *aclGrants, int *xmlDocumentLenReturn, char *xmlDocument, int xmlDocumentBufferSize) { *xmlDocumentLenReturn = 0; #define append(fmt, ...) \ do { \ *xmlDocumentLenReturn += snprintf \ (&(xmlDocument[*xmlDocumentLenReturn]), \ xmlDocumentBufferSize - *xmlDocumentLenReturn - 1, \ fmt, __VA_ARGS__); \ if (*xmlDocumentLenReturn >= xmlDocumentBufferSize) { \ return S3StatusXmlDocumentTooLarge; \ } \ } while (0) append("%s%s" "", ownerId, ownerDisplayName); int i; for (i = 0; i < aclGrantCount; i++) { append("%s", "granteeType) { case S3GranteeTypeAmazonCustomerByEmail: append("AmazonCustomerByEmail\">%s", grant->grantee.amazonCustomerByEmail.emailAddress); break; case S3GranteeTypeCanonicalUser: append("CanonicalUser\">%s%s", grant->grantee.canonicalUser.id, grant->grantee.canonicalUser.displayName); break; default: { // case S3GranteeTypeAllAwsUsers/S3GranteeTypeAllUsers: const char *grantee; switch (grant->granteeType) { case S3GranteeTypeAllAwsUsers: grantee = ACS_GROUP_AWS_USERS; break; case S3GranteeTypeAllUsers: grantee = ACS_GROUP_ALL_USERS; break; default: grantee = ACS_GROUP_LOG_DELIVERY; break; } append("Group\">%s", grantee); } break; } append("%s", ((grant->permission == S3PermissionRead) ? "READ" : (grant->permission == S3PermissionWrite) ? "WRITE" : (grant->permission == S3PermissionReadACP) ? "READ_ACP" : (grant->permission == S3PermissionWriteACP) ? "WRITE_ACP" : "FULL_CONTROL")); } append("%s", ""); return S3StatusOK; } typedef struct SetAclData { S3ResponsePropertiesCallback *responsePropertiesCallback; S3ResponseCompleteCallback *responseCompleteCallback; void *callbackData; int aclXmlDocumentLen; char aclXmlDocument[ACL_XML_DOC_MAXSIZE]; int aclXmlDocumentBytesWritten; } SetAclData; static S3Status setAclPropertiesCallback (const S3ResponseProperties *responseProperties, void *callbackData) { SetAclData *paData = (SetAclData *) callbackData; return (*(paData->responsePropertiesCallback)) (responseProperties, paData->callbackData); } static int setAclDataCallback(int bufferSize, char *buffer, void *callbackData) { SetAclData *paData = (SetAclData *) callbackData; int remaining = (paData->aclXmlDocumentLen - paData->aclXmlDocumentBytesWritten); int toCopy = bufferSize > remaining ? remaining : bufferSize; if (!toCopy) { return 0; } memcpy(buffer, &(paData->aclXmlDocument [paData->aclXmlDocumentBytesWritten]), toCopy); paData->aclXmlDocumentBytesWritten += toCopy; return toCopy; } static void setAclCompleteCallback(S3Status requestStatus, const S3ErrorDetails *s3ErrorDetails, void *callbackData) { SetAclData *paData = (SetAclData *) callbackData; (*(paData->responseCompleteCallback)) (requestStatus, s3ErrorDetails, paData->callbackData); free(paData); } void S3_set_acl(const S3BucketContext *bucketContext, const char *key, const char *ownerId, const char *ownerDisplayName, int aclGrantCount, const S3AclGrant *aclGrants, S3RequestContext *requestContext, const S3ResponseHandler *handler, void *callbackData) { if (aclGrantCount > S3_MAX_ACL_GRANT_COUNT) { (*(handler->completeCallback)) (S3StatusTooManyGrants, 0, callbackData); return; } SetAclData *data = (SetAclData *) malloc(sizeof(SetAclData)); if (!data) { (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData); return; } // Convert aclGrants to XML document S3Status status = generateAclXmlDocument (ownerId, ownerDisplayName, aclGrantCount, aclGrants, &(data->aclXmlDocumentLen), data->aclXmlDocument, sizeof(data->aclXmlDocument)); if (status != S3StatusOK) { free(data); (*(handler->completeCallback))(status, 0, callbackData); return; } data->responsePropertiesCallback = handler->propertiesCallback; data->responseCompleteCallback = handler->completeCallback; data->callbackData = callbackData; data->aclXmlDocumentBytesWritten = 0; // Set up the RequestParams RequestParams params = { HttpRequestTypePUT, // httpRequestType { bucketContext->hostName, // hostName bucketContext->bucketName, // bucketName bucketContext->protocol, // protocol bucketContext->uriStyle, // uriStyle bucketContext->accessKeyId, // accessKeyId bucketContext->secretAccessKey }, // secretAccessKey key, // key 0, // queryParams "acl", // subResource 0, // copySourceBucketName 0, // copySourceKey 0, // getConditions 0, // startByte 0, // byteCount 0, // putProperties &setAclPropertiesCallback, // propertiesCallback &setAclDataCallback, // toS3Callback data->aclXmlDocumentLen, // toS3CallbackTotalSize 0, // fromS3Callback &setAclCompleteCallback, // completeCallback data // callbackData }; // Perform the request request_perform(¶ms, requestContext); } libs3-2.0/src/mingw_functions.c0000644000175000001440000000532611640553711015227 0ustar bjiusers/** ************************************************************************** * mingw_functions.c * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ #include #include unsigned long pthread_self() { return (unsigned long) GetCurrentThreadId(); } int pthread_mutex_init(pthread_mutex_t *mutex, void *v) { (void) v; InitializeCriticalSection(&(mutex->criticalSection)); return 0; } int pthread_mutex_lock(pthread_mutex_t *mutex) { EnterCriticalSection(&(mutex->criticalSection)); return 0; } int pthread_mutex_unlock(pthread_mutex_t *mutex) { LeaveCriticalSection(&(mutex->criticalSection)); return 0; } int pthread_mutex_destroy(pthread_mutex_t *mutex) { DeleteCriticalSection(&(mutex->criticalSection)); return 0; } int uname(struct utsname *u) { OSVERSIONINFO info; info.dwOSVersionInfoSize = sizeof(info); if (!GetVersionEx(&info)) { return -1; } u->machine = ""; switch (info.dwMajorVersion) { case 4: switch (info.dwMinorVersion) { case 0: u->sysname = "Microsoft Windows NT 4.0"; break; case 10: u->sysname = "Microsoft Windows 98"; break; case 90: u->sysname = "Microsoft Windows Me"; break; default: return -1; } break; case 5: switch (info.dwMinorVersion) { case 0: u->sysname = "Microsoft Windows 2000"; break; case 1: u->sysname = "Microsoft Windows XP"; break; case 2: u->sysname = "Microsoft Server 2003"; break; default: return -1; } break; default: return -1; } return 0; } libs3-2.0/src/bucket.c0000644000175000001440000007117011640553711013273 0ustar bjiusers/** ************************************************************************** * bucket.c * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ #include #include #include "libs3.h" #include "request.h" #include "simplexml.h" // test bucket --------------------------------------------------------------- typedef struct TestBucketData { SimpleXml simpleXml; S3ResponsePropertiesCallback *responsePropertiesCallback; S3ResponseCompleteCallback *responseCompleteCallback; void *callbackData; int locationConstraintReturnSize; char *locationConstraintReturn; string_buffer(locationConstraint, 256); } TestBucketData; static S3Status testBucketXmlCallback(const char *elementPath, const char *data, int dataLen, void *callbackData) { TestBucketData *tbData = (TestBucketData *) callbackData; int fit; if (data && !strcmp(elementPath, "LocationConstraint")) { string_buffer_append(tbData->locationConstraint, data, dataLen, fit); } /* Avoid compiler error about variable set but not used */ (void) fit; return S3StatusOK; } static S3Status testBucketPropertiesCallback (const S3ResponseProperties *responseProperties, void *callbackData) { TestBucketData *tbData = (TestBucketData *) callbackData; return (*(tbData->responsePropertiesCallback)) (responseProperties, tbData->callbackData); } static S3Status testBucketDataCallback(int bufferSize, const char *buffer, void *callbackData) { TestBucketData *tbData = (TestBucketData *) callbackData; return simplexml_add(&(tbData->simpleXml), buffer, bufferSize); } static void testBucketCompleteCallback(S3Status requestStatus, const S3ErrorDetails *s3ErrorDetails, void *callbackData) { TestBucketData *tbData = (TestBucketData *) callbackData; // Copy the location constraint into the return buffer snprintf(tbData->locationConstraintReturn, tbData->locationConstraintReturnSize, "%s", tbData->locationConstraint); (*(tbData->responseCompleteCallback)) (requestStatus, s3ErrorDetails, tbData->callbackData); simplexml_deinitialize(&(tbData->simpleXml)); free(tbData); } void S3_test_bucket(S3Protocol protocol, S3UriStyle uriStyle, const char *accessKeyId, const char *secretAccessKey, const char *hostName, const char *bucketName, int locationConstraintReturnSize, char *locationConstraintReturn, S3RequestContext *requestContext, const S3ResponseHandler *handler, void *callbackData) { // Create the callback data TestBucketData *tbData = (TestBucketData *) malloc(sizeof(TestBucketData)); if (!tbData) { (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData); return; } simplexml_initialize(&(tbData->simpleXml), &testBucketXmlCallback, tbData); tbData->responsePropertiesCallback = handler->propertiesCallback; tbData->responseCompleteCallback = handler->completeCallback; tbData->callbackData = callbackData; tbData->locationConstraintReturnSize = locationConstraintReturnSize; tbData->locationConstraintReturn = locationConstraintReturn; string_buffer_initialize(tbData->locationConstraint); // Set up the RequestParams RequestParams params = { HttpRequestTypeGET, // httpRequestType { hostName, // hostName bucketName, // bucketName protocol, // protocol uriStyle, // uriStyle accessKeyId, // accessKeyId secretAccessKey }, // secretAccessKey 0, // key 0, // queryParams "location", // subResource 0, // copySourceBucketName 0, // copySourceKey 0, // getConditions 0, // startByte 0, // byteCount 0, // putProperties &testBucketPropertiesCallback, // propertiesCallback 0, // toS3Callback 0, // toS3CallbackTotalSize &testBucketDataCallback, // fromS3Callback &testBucketCompleteCallback, // completeCallback tbData // callbackData }; // Perform the request request_perform(¶ms, requestContext); } // create bucket ------------------------------------------------------------- typedef struct CreateBucketData { S3ResponsePropertiesCallback *responsePropertiesCallback; S3ResponseCompleteCallback *responseCompleteCallback; void *callbackData; char doc[1024]; int docLen, docBytesWritten; } CreateBucketData; static S3Status createBucketPropertiesCallback (const S3ResponseProperties *responseProperties, void *callbackData) { CreateBucketData *cbData = (CreateBucketData *) callbackData; return (*(cbData->responsePropertiesCallback)) (responseProperties, cbData->callbackData); } static int createBucketDataCallback(int bufferSize, char *buffer, void *callbackData) { CreateBucketData *cbData = (CreateBucketData *) callbackData; if (!cbData->docLen) { return 0; } int remaining = (cbData->docLen - cbData->docBytesWritten); int toCopy = bufferSize > remaining ? remaining : bufferSize; if (!toCopy) { return 0; } memcpy(buffer, &(cbData->doc[cbData->docBytesWritten]), toCopy); cbData->docBytesWritten += toCopy; return toCopy; } static void createBucketCompleteCallback(S3Status requestStatus, const S3ErrorDetails *s3ErrorDetails, void *callbackData) { CreateBucketData *cbData = (CreateBucketData *) callbackData; (*(cbData->responseCompleteCallback)) (requestStatus, s3ErrorDetails, cbData->callbackData); free(cbData); } void S3_create_bucket(S3Protocol protocol, const char *accessKeyId, const char *secretAccessKey, const char *hostName, const char *bucketName, S3CannedAcl cannedAcl, const char *locationConstraint, S3RequestContext *requestContext, const S3ResponseHandler *handler, void *callbackData) { // Create the callback data CreateBucketData *cbData = (CreateBucketData *) malloc(sizeof(CreateBucketData)); if (!cbData) { (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData); return; } cbData->responsePropertiesCallback = handler->propertiesCallback; cbData->responseCompleteCallback = handler->completeCallback; cbData->callbackData = callbackData; if (locationConstraint) { cbData->docLen = snprintf(cbData->doc, sizeof(cbData->doc), "" "%s", locationConstraint); cbData->docBytesWritten = 0; } else { cbData->docLen = 0; } // Set up S3PutProperties S3PutProperties properties = { 0, // contentType 0, // md5 0, // cacheControl 0, // contentDispositionFilename 0, // contentEncoding 0, // expires cannedAcl, // cannedAcl 0, // metaDataCount 0 // metaData }; // Set up the RequestParams RequestParams params = { HttpRequestTypePUT, // httpRequestType { hostName, // hostName bucketName, // bucketName protocol, // protocol S3UriStylePath, // uriStyle accessKeyId, // accessKeyId secretAccessKey }, // secretAccessKey 0, // key 0, // queryParams 0, // subResource 0, // copySourceBucketName 0, // copySourceKey 0, // getConditions 0, // startByte 0, // byteCount &properties, // putProperties &createBucketPropertiesCallback, // propertiesCallback &createBucketDataCallback, // toS3Callback cbData->docLen, // toS3CallbackTotalSize 0, // fromS3Callback &createBucketCompleteCallback, // completeCallback cbData // callbackData }; // Perform the request request_perform(¶ms, requestContext); } // delete bucket ------------------------------------------------------------- typedef struct DeleteBucketData { S3ResponsePropertiesCallback *responsePropertiesCallback; S3ResponseCompleteCallback *responseCompleteCallback; void *callbackData; } DeleteBucketData; static S3Status deleteBucketPropertiesCallback (const S3ResponseProperties *responseProperties, void *callbackData) { DeleteBucketData *dbData = (DeleteBucketData *) callbackData; return (*(dbData->responsePropertiesCallback)) (responseProperties, dbData->callbackData); } static void deleteBucketCompleteCallback(S3Status requestStatus, const S3ErrorDetails *s3ErrorDetails, void *callbackData) { DeleteBucketData *dbData = (DeleteBucketData *) callbackData; (*(dbData->responseCompleteCallback)) (requestStatus, s3ErrorDetails, dbData->callbackData); free(dbData); } void S3_delete_bucket(S3Protocol protocol, S3UriStyle uriStyle, const char *accessKeyId, const char *secretAccessKey, const char *hostName, const char *bucketName, S3RequestContext *requestContext, const S3ResponseHandler *handler, void *callbackData) { // Create the callback data DeleteBucketData *dbData = (DeleteBucketData *) malloc(sizeof(DeleteBucketData)); if (!dbData) { (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData); return; } dbData->responsePropertiesCallback = handler->propertiesCallback; dbData->responseCompleteCallback = handler->completeCallback; dbData->callbackData = callbackData; // Set up the RequestParams RequestParams params = { HttpRequestTypeDELETE, // httpRequestType { hostName, // hostName bucketName, // bucketName protocol, // protocol uriStyle, // uriStyle accessKeyId, // accessKeyId secretAccessKey }, // secretAccessKey 0, // key 0, // queryParams 0, // subResource 0, // copySourceBucketName 0, // copySourceKey 0, // getConditions 0, // startByte 0, // byteCount 0, // putProperties &deleteBucketPropertiesCallback, // propertiesCallback 0, // toS3Callback 0, // toS3CallbackTotalSize 0, // fromS3Callback &deleteBucketCompleteCallback, // completeCallback dbData // callbackData }; // Perform the request request_perform(¶ms, requestContext); } // list bucket ---------------------------------------------------------------- typedef struct ListBucketContents { string_buffer(key, 1024); string_buffer(lastModified, 256); string_buffer(eTag, 256); string_buffer(size, 24); string_buffer(ownerId, 256); string_buffer(ownerDisplayName, 256); } ListBucketContents; static void initialize_list_bucket_contents(ListBucketContents *contents) { string_buffer_initialize(contents->key); string_buffer_initialize(contents->lastModified); string_buffer_initialize(contents->eTag); string_buffer_initialize(contents->size); string_buffer_initialize(contents->ownerId); string_buffer_initialize(contents->ownerDisplayName); } // We read up to 32 Contents at a time #define MAX_CONTENTS 32 // We read up to 8 CommonPrefixes at a time #define MAX_COMMON_PREFIXES 8 typedef struct ListBucketData { SimpleXml simpleXml; S3ResponsePropertiesCallback *responsePropertiesCallback; S3ListBucketCallback *listBucketCallback; S3ResponseCompleteCallback *responseCompleteCallback; void *callbackData; string_buffer(isTruncated, 64); string_buffer(nextMarker, 1024); int contentsCount; ListBucketContents contents[MAX_CONTENTS]; int commonPrefixesCount; char commonPrefixes[MAX_COMMON_PREFIXES][1024]; int commonPrefixLens[MAX_COMMON_PREFIXES]; } ListBucketData; static void initialize_list_bucket_data(ListBucketData *lbData) { lbData->contentsCount = 0; initialize_list_bucket_contents(lbData->contents); lbData->commonPrefixesCount = 0; lbData->commonPrefixes[0][0] = 0; lbData->commonPrefixLens[0] = 0; } static S3Status make_list_bucket_callback(ListBucketData *lbData) { int i; // Convert IsTruncated int isTruncated = (!strcmp(lbData->isTruncated, "true") || !strcmp(lbData->isTruncated, "1")) ? 1 : 0; // Convert the contents S3ListBucketContent contents[lbData->contentsCount]; int contentsCount = lbData->contentsCount; for (i = 0; i < contentsCount; i++) { S3ListBucketContent *contentDest = &(contents[i]); ListBucketContents *contentSrc = &(lbData->contents[i]); contentDest->key = contentSrc->key; contentDest->lastModified = parseIso8601Time(contentSrc->lastModified); contentDest->eTag = contentSrc->eTag; contentDest->size = parseUnsignedInt(contentSrc->size); contentDest->ownerId = contentSrc->ownerId[0] ?contentSrc->ownerId : 0; contentDest->ownerDisplayName = (contentSrc->ownerDisplayName[0] ? contentSrc->ownerDisplayName : 0); } // Make the common prefixes array int commonPrefixesCount = lbData->commonPrefixesCount; char *commonPrefixes[commonPrefixesCount]; for (i = 0; i < commonPrefixesCount; i++) { commonPrefixes[i] = lbData->commonPrefixes[i]; } return (*(lbData->listBucketCallback)) (isTruncated, lbData->nextMarker, contentsCount, contents, commonPrefixesCount, (const char **) commonPrefixes, lbData->callbackData); } static S3Status listBucketXmlCallback(const char *elementPath, const char *data, int dataLen, void *callbackData) { ListBucketData *lbData = (ListBucketData *) callbackData; int fit; if (data) { if (!strcmp(elementPath, "ListBucketResult/IsTruncated")) { string_buffer_append(lbData->isTruncated, data, dataLen, fit); } else if (!strcmp(elementPath, "ListBucketResult/NextMarker")) { string_buffer_append(lbData->nextMarker, data, dataLen, fit); } else if (!strcmp(elementPath, "ListBucketResult/Contents/Key")) { ListBucketContents *contents = &(lbData->contents[lbData->contentsCount]); string_buffer_append(contents->key, data, dataLen, fit); } else if (!strcmp(elementPath, "ListBucketResult/Contents/LastModified")) { ListBucketContents *contents = &(lbData->contents[lbData->contentsCount]); string_buffer_append(contents->lastModified, data, dataLen, fit); } else if (!strcmp(elementPath, "ListBucketResult/Contents/ETag")) { ListBucketContents *contents = &(lbData->contents[lbData->contentsCount]); string_buffer_append(contents->eTag, data, dataLen, fit); } else if (!strcmp(elementPath, "ListBucketResult/Contents/Size")) { ListBucketContents *contents = &(lbData->contents[lbData->contentsCount]); string_buffer_append(contents->size, data, dataLen, fit); } else if (!strcmp(elementPath, "ListBucketResult/Contents/Owner/ID")) { ListBucketContents *contents = &(lbData->contents[lbData->contentsCount]); string_buffer_append(contents->ownerId, data, dataLen, fit); } else if (!strcmp(elementPath, "ListBucketResult/Contents/Owner/DisplayName")) { ListBucketContents *contents = &(lbData->contents[lbData->contentsCount]); string_buffer_append (contents->ownerDisplayName, data, dataLen, fit); } else if (!strcmp(elementPath, "ListBucketResult/CommonPrefixes/Prefix")) { int which = lbData->commonPrefixesCount; lbData->commonPrefixLens[which] += snprintf(lbData->commonPrefixes[which], sizeof(lbData->commonPrefixes[which]) - lbData->commonPrefixLens[which] - 1, "%.*s", dataLen, data); if (lbData->commonPrefixLens[which] >= (int) sizeof(lbData->commonPrefixes[which])) { return S3StatusXmlParseFailure; } } } else { if (!strcmp(elementPath, "ListBucketResult/Contents")) { // Finished a Contents lbData->contentsCount++; if (lbData->contentsCount == MAX_CONTENTS) { // Make the callback S3Status status = make_list_bucket_callback(lbData); if (status != S3StatusOK) { return status; } initialize_list_bucket_data(lbData); } else { // Initialize the next one initialize_list_bucket_contents (&(lbData->contents[lbData->contentsCount])); } } else if (!strcmp(elementPath, "ListBucketResult/CommonPrefixes/Prefix")) { // Finished a Prefix lbData->commonPrefixesCount++; if (lbData->commonPrefixesCount == MAX_COMMON_PREFIXES) { // Make the callback S3Status status = make_list_bucket_callback(lbData); if (status != S3StatusOK) { return status; } initialize_list_bucket_data(lbData); } else { // Initialize the next one lbData->commonPrefixes[lbData->commonPrefixesCount][0] = 0; lbData->commonPrefixLens[lbData->commonPrefixesCount] = 0; } } } /* Avoid compiler error about variable set but not used */ (void) fit; return S3StatusOK; } static S3Status listBucketPropertiesCallback (const S3ResponseProperties *responseProperties, void *callbackData) { ListBucketData *lbData = (ListBucketData *) callbackData; return (*(lbData->responsePropertiesCallback)) (responseProperties, lbData->callbackData); } static S3Status listBucketDataCallback(int bufferSize, const char *buffer, void *callbackData) { ListBucketData *lbData = (ListBucketData *) callbackData; return simplexml_add(&(lbData->simpleXml), buffer, bufferSize); } static void listBucketCompleteCallback(S3Status requestStatus, const S3ErrorDetails *s3ErrorDetails, void *callbackData) { ListBucketData *lbData = (ListBucketData *) callbackData; // Make the callback if there is anything if (lbData->contentsCount || lbData->commonPrefixesCount) { make_list_bucket_callback(lbData); } (*(lbData->responseCompleteCallback)) (requestStatus, s3ErrorDetails, lbData->callbackData); simplexml_deinitialize(&(lbData->simpleXml)); free(lbData); } void S3_list_bucket(const S3BucketContext *bucketContext, const char *prefix, const char *marker, const char *delimiter, int maxkeys, S3RequestContext *requestContext, const S3ListBucketHandler *handler, void *callbackData) { // Compose the query params string_buffer(queryParams, 4096); string_buffer_initialize(queryParams); #define safe_append(name, value) \ do { \ int fit; \ if (amp) { \ string_buffer_append(queryParams, "&", 1, fit); \ if (!fit) { \ (*(handler->responseHandler.completeCallback)) \ (S3StatusQueryParamsTooLong, 0, callbackData); \ return; \ } \ } \ string_buffer_append(queryParams, name "=", \ sizeof(name "=") - 1, fit); \ if (!fit) { \ (*(handler->responseHandler.completeCallback)) \ (S3StatusQueryParamsTooLong, 0, callbackData); \ return; \ } \ amp = 1; \ char encoded[3 * 1024]; \ if (!urlEncode(encoded, value, 1024)) { \ (*(handler->responseHandler.completeCallback)) \ (S3StatusQueryParamsTooLong, 0, callbackData); \ return; \ } \ string_buffer_append(queryParams, encoded, strlen(encoded), \ fit); \ if (!fit) { \ (*(handler->responseHandler.completeCallback)) \ (S3StatusQueryParamsTooLong, 0, callbackData); \ return; \ } \ } while (0) int amp = 0; if (prefix) { safe_append("prefix", prefix); } if (marker) { safe_append("marker", marker); } if (delimiter) { safe_append("delimiter", delimiter); } if (maxkeys) { char maxKeysString[64]; snprintf(maxKeysString, sizeof(maxKeysString), "%d", maxkeys); safe_append("max-keys", maxKeysString); } ListBucketData *lbData = (ListBucketData *) malloc(sizeof(ListBucketData)); if (!lbData) { (*(handler->responseHandler.completeCallback)) (S3StatusOutOfMemory, 0, callbackData); return; } simplexml_initialize(&(lbData->simpleXml), &listBucketXmlCallback, lbData); lbData->responsePropertiesCallback = handler->responseHandler.propertiesCallback; lbData->listBucketCallback = handler->listBucketCallback; lbData->responseCompleteCallback = handler->responseHandler.completeCallback; lbData->callbackData = callbackData; string_buffer_initialize(lbData->isTruncated); string_buffer_initialize(lbData->nextMarker); initialize_list_bucket_data(lbData); // Set up the RequestParams RequestParams params = { HttpRequestTypeGET, // httpRequestType { bucketContext->hostName, // hostName bucketContext->bucketName, // bucketName bucketContext->protocol, // protocol bucketContext->uriStyle, // uriStyle bucketContext->accessKeyId, // accessKeyId bucketContext->secretAccessKey }, // secretAccessKey 0, // key queryParams[0] ? queryParams : 0, // queryParams 0, // subResource 0, // copySourceBucketName 0, // copySourceKey 0, // getConditions 0, // startByte 0, // byteCount 0, // putProperties &listBucketPropertiesCallback, // propertiesCallback 0, // toS3Callback 0, // toS3CallbackTotalSize &listBucketDataCallback, // fromS3Callback &listBucketCompleteCallback, // completeCallback lbData // callbackData }; // Perform the request request_perform(¶ms, requestContext); } libs3-2.0/src/mingw_s3_functions.c0000644000175000001440000000241311640553711015626 0ustar bjiusers/** ************************************************************************** * mingw_s3_functions.c * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ int setenv(const char *a, const char *b, int c) { (void) c; return SetEnvironmentVariable(a, b); } int unsetenv(const char *a) { return SetEnvironmentVariable(a, 0); } libs3-2.0/src/service.c0000644000175000001440000001572411640553711013461 0ustar bjiusers/** ************************************************************************** * service.c * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ #include #include #include #include #include "request.h" typedef struct XmlCallbackData { SimpleXml simpleXml; S3ResponsePropertiesCallback *responsePropertiesCallback; S3ListServiceCallback *listServiceCallback; S3ResponseCompleteCallback *responseCompleteCallback; void *callbackData; string_buffer(ownerId, 256); string_buffer(ownerDisplayName, 256); string_buffer(bucketName, 256); string_buffer(creationDate, 128); } XmlCallbackData; static S3Status xmlCallback(const char *elementPath, const char *data, int dataLen, void *callbackData) { XmlCallbackData *cbData = (XmlCallbackData *) callbackData; int fit; if (data) { if (!strcmp(elementPath, "ListAllMyBucketsResult/Owner/ID")) { string_buffer_append(cbData->ownerId, data, dataLen, fit); } else if (!strcmp(elementPath, "ListAllMyBucketsResult/Owner/DisplayName")) { string_buffer_append(cbData->ownerDisplayName, data, dataLen, fit); } else if (!strcmp(elementPath, "ListAllMyBucketsResult/Buckets/Bucket/Name")) { string_buffer_append(cbData->bucketName, data, dataLen, fit); } else if (!strcmp (elementPath, "ListAllMyBucketsResult/Buckets/Bucket/CreationDate")) { string_buffer_append(cbData->creationDate, data, dataLen, fit); } } else { if (!strcmp(elementPath, "ListAllMyBucketsResult/Buckets/Bucket")) { // Parse date. Assume ISO-8601 date format. time_t creationDate = parseIso8601Time(cbData->creationDate); // Make the callback - a bucket just finished S3Status status = (*(cbData->listServiceCallback)) (cbData->ownerId, cbData->ownerDisplayName, cbData->bucketName, creationDate, cbData->callbackData); string_buffer_initialize(cbData->bucketName); string_buffer_initialize(cbData->creationDate); return status; } } /* Avoid compiler error about variable set but not used */ (void) fit; return S3StatusOK; } static S3Status propertiesCallback (const S3ResponseProperties *responseProperties, void *callbackData) { XmlCallbackData *cbData = (XmlCallbackData *) callbackData; return (*(cbData->responsePropertiesCallback)) (responseProperties, cbData->callbackData); } static S3Status dataCallback(int bufferSize, const char *buffer, void *callbackData) { XmlCallbackData *cbData = (XmlCallbackData *) callbackData; return simplexml_add(&(cbData->simpleXml), buffer, bufferSize); } static void completeCallback(S3Status requestStatus, const S3ErrorDetails *s3ErrorDetails, void *callbackData) { XmlCallbackData *cbData = (XmlCallbackData *) callbackData; (*(cbData->responseCompleteCallback)) (requestStatus, s3ErrorDetails, cbData->callbackData); simplexml_deinitialize(&(cbData->simpleXml)); free(cbData); } void S3_list_service(S3Protocol protocol, const char *accessKeyId, const char *secretAccessKey, const char *hostName, S3RequestContext *requestContext, const S3ListServiceHandler *handler, void *callbackData) { // Create and set up the callback data XmlCallbackData *data = (XmlCallbackData *) malloc(sizeof(XmlCallbackData)); if (!data) { (*(handler->responseHandler.completeCallback)) (S3StatusOutOfMemory, 0, callbackData); return; } simplexml_initialize(&(data->simpleXml), &xmlCallback, data); data->responsePropertiesCallback = handler->responseHandler.propertiesCallback; data->listServiceCallback = handler->listServiceCallback; data->responseCompleteCallback = handler->responseHandler.completeCallback; data->callbackData = callbackData; string_buffer_initialize(data->ownerId); string_buffer_initialize(data->ownerDisplayName); string_buffer_initialize(data->bucketName); string_buffer_initialize(data->creationDate); // Set up the RequestParams RequestParams params = { HttpRequestTypeGET, // httpRequestType { hostName, // hostName 0, // bucketName protocol, // protocol S3UriStylePath, // uriStyle accessKeyId, // accessKeyId secretAccessKey }, // secretAccessKey 0, // key 0, // queryParams 0, // subResource 0, // copySourceBucketName 0, // copySourceKey 0, // getConditions 0, // startByte 0, // byteCount 0, // requestProperties &propertiesCallback, // propertiesCallback 0, // toS3Callback 0, // toS3CallbackTotalSize &dataCallback, // fromS3Callback &completeCallback, // completeCallback data // callbackData }; // Perform the request request_perform(¶ms, requestContext); } libs3-2.0/src/util.c0000644000175000001440000003662211640553711012776 0ustar bjiusers/** ************************************************************************** * util.c * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ #include #include #include "util.h" // Convenience utility for making the code look nicer. Tests a string // against a format; only the characters specified in the format are // checked (i.e. if the string is longer than the format, the string still // checks out ok). Format characters are: // d - is a digit // anything else - is that character // Returns nonzero the string checks out, zero if it does not. static int checkString(const char *str, const char *format) { while (*format) { if (*format == 'd') { if (!isdigit(*str)) { return 0; } } else if (*str != *format) { return 0; } str++, format++; } return 1; } int urlEncode(char *dest, const char *src, int maxSrcSize) { static const char *hex = "0123456789ABCDEF"; int len = 0; if (src) while (*src) { if (++len > maxSrcSize) { *dest = 0; return 0; } unsigned char c = *src; if (isalnum(c) || (c == '-') || (c == '_') || (c == '.') || (c == '!') || (c == '~') || (c == '*') || (c == '\'') || (c == '(') || (c == ')') || (c == '/')) { *dest++ = c; } else if (*src == ' ') { *dest++ = '+'; } else { *dest++ = '%'; *dest++ = hex[c >> 4]; *dest++ = hex[c & 15]; } src++; } *dest = 0; return 1; } int64_t parseIso8601Time(const char *str) { // Check to make sure that it has a valid format if (!checkString(str, "dddd-dd-ddTdd:dd:dd")) { return -1; } #define nextnum() (((*str - '0') * 10) + (*(str + 1) - '0')) // Convert it struct tm stm; memset(&stm, 0, sizeof(stm)); stm.tm_year = (nextnum() - 19) * 100; str += 2; stm.tm_year += nextnum(); str += 3; stm.tm_mon = nextnum() - 1; str += 3; stm.tm_mday = nextnum(); str += 3; stm.tm_hour = nextnum(); str += 3; stm.tm_min = nextnum(); str += 3; stm.tm_sec = nextnum(); str += 2; stm.tm_isdst = -1; int64_t ret = mktime(&stm); // Skip the millis if (*str == '.') { str++; while (isdigit(*str)) { str++; } } if (checkString(str, "-dd:dd") || checkString(str, "+dd:dd")) { int sign = (*str++ == '-') ? -1 : 1; int hours = nextnum(); str += 3; int minutes = nextnum(); ret += (-sign * (((hours * 60) + minutes) * 60)); } // Else it should be Z to be a conformant time string, but we just assume // that it is rather than enforcing that return ret; } uint64_t parseUnsignedInt(const char *str) { // Skip whitespace while (is_blank(*str)) { str++; } uint64_t ret = 0; while (isdigit(*str)) { ret *= 10; ret += (*str++ - '0'); } return ret; } int base64Encode(const unsigned char *in, int inLen, char *out) { static const char *ENC = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; char *original_out = out; while (inLen) { // first 6 bits of char 1 *out++ = ENC[*in >> 2]; if (!--inLen) { // last 2 bits of char 1, 4 bits of 0 *out++ = ENC[(*in & 0x3) << 4]; *out++ = '='; *out++ = '='; break; } // last 2 bits of char 1, first 4 bits of char 2 *out++ = ENC[((*in & 0x3) << 4) | (*(in + 1) >> 4)]; in++; if (!--inLen) { // last 4 bits of char 2, 2 bits of 0 *out++ = ENC[(*in & 0xF) << 2]; *out++ = '='; break; } // last 4 bits of char 2, first 2 bits of char 3 *out++ = ENC[((*in & 0xF) << 2) | (*(in + 1) >> 6)]; in++; // last 6 bits of char 3 *out++ = ENC[*in & 0x3F]; in++, inLen--; } return (out - original_out); } #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) #define blk0L(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) \ | (rol(block->l[i], 8) & 0x00FF00FF)) #define blk0B(i) (block->l[i]) #define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ \ block->l[(i + 8) & 15] ^ \ block->l[(i + 2) & 15] ^ \ block->l[i & 15], 1)) #define R0_L(v, w, x, y, z, i) \ z += ((w & (x ^ y)) ^ y) + blk0L(i) + 0x5A827999 + rol(v, 5); \ w = rol(w, 30); #define R0_B(v, w, x, y, z, i) \ z += ((w & (x ^ y)) ^ y) + blk0B(i) + 0x5A827999 + rol(v, 5); \ w = rol(w, 30); #define R1(v, w, x, y, z, i) \ z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \ w = rol(w, 30); #define R2(v, w, x, y, z, i) \ z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \ w = rol(w, 30); #define R3(v, w, x, y, z, i) \ z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \ w = rol(w, 30); #define R4(v, w, x, y, z, i) \ z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \ w = rol(w, 30); #define R0A_L(i) R0_L(a, b, c, d, e, i) #define R0B_L(i) R0_L(b, c, d, e, a, i) #define R0C_L(i) R0_L(c, d, e, a, b, i) #define R0D_L(i) R0_L(d, e, a, b, c, i) #define R0E_L(i) R0_L(e, a, b, c, d, i) #define R0A_B(i) R0_B(a, b, c, d, e, i) #define R0B_B(i) R0_B(b, c, d, e, a, i) #define R0C_B(i) R0_B(c, d, e, a, b, i) #define R0D_B(i) R0_B(d, e, a, b, c, i) #define R0E_B(i) R0_B(e, a, b, c, d, i) #define R1A(i) R1(a, b, c, d, e, i) #define R1B(i) R1(b, c, d, e, a, i) #define R1C(i) R1(c, d, e, a, b, i) #define R1D(i) R1(d, e, a, b, c, i) #define R1E(i) R1(e, a, b, c, d, i) #define R2A(i) R2(a, b, c, d, e, i) #define R2B(i) R2(b, c, d, e, a, i) #define R2C(i) R2(c, d, e, a, b, i) #define R2D(i) R2(d, e, a, b, c, i) #define R2E(i) R2(e, a, b, c, d, i) #define R3A(i) R3(a, b, c, d, e, i) #define R3B(i) R3(b, c, d, e, a, i) #define R3C(i) R3(c, d, e, a, b, i) #define R3D(i) R3(d, e, a, b, c, i) #define R3E(i) R3(e, a, b, c, d, i) #define R4A(i) R4(a, b, c, d, e, i) #define R4B(i) R4(b, c, d, e, a, i) #define R4C(i) R4(c, d, e, a, b, i) #define R4D(i) R4(d, e, a, b, c, i) #define R4E(i) R4(e, a, b, c, d, i) static void SHA1_transform(uint32_t state[5], const unsigned char buffer[64]) { uint32_t a, b, c, d, e; typedef union { unsigned char c[64]; uint32_t l[16]; } u; unsigned char w[64]; u *block = (u *) w; memcpy(block, buffer, 64); a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; static uint32_t endianness_indicator = 0x1; if (((unsigned char *) &endianness_indicator)[0]) { R0A_L( 0); R0E_L( 1); R0D_L( 2); R0C_L( 3); R0B_L( 4); R0A_L( 5); R0E_L( 6); R0D_L( 7); R0C_L( 8); R0B_L( 9); R0A_L(10); R0E_L(11); R0D_L(12); R0C_L(13); R0B_L(14); R0A_L(15); } else { R0A_B( 0); R0E_B( 1); R0D_B( 2); R0C_B( 3); R0B_B( 4); R0A_B( 5); R0E_B( 6); R0D_B( 7); R0C_B( 8); R0B_B( 9); R0A_B(10); R0E_B(11); R0D_B(12); R0C_B(13); R0B_B(14); R0A_B(15); } R1E(16); R1D(17); R1C(18); R1B(19); R2A(20); R2E(21); R2D(22); R2C(23); R2B(24); R2A(25); R2E(26); R2D(27); R2C(28); R2B(29); R2A(30); R2E(31); R2D(32); R2C(33); R2B(34); R2A(35); R2E(36); R2D(37); R2C(38); R2B(39); R3A(40); R3E(41); R3D(42); R3C(43); R3B(44); R3A(45); R3E(46); R3D(47); R3C(48); R3B(49); R3A(50); R3E(51); R3D(52); R3C(53); R3B(54); R3A(55); R3E(56); R3D(57); R3C(58); R3B(59); R4A(60); R4E(61); R4D(62); R4C(63); R4B(64); R4A(65); R4E(66); R4D(67); R4C(68); R4B(69); R4A(70); R4E(71); R4D(72); R4C(73); R4B(74); R4A(75); R4E(76); R4D(77); R4C(78); R4B(79); state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; } typedef struct { uint32_t state[5]; uint32_t count[2]; unsigned char buffer[64]; } SHA1Context; static void SHA1_init(SHA1Context *context) { context->state[0] = 0x67452301; context->state[1] = 0xEFCDAB89; context->state[2] = 0x98BADCFE; context->state[3] = 0x10325476; context->state[4] = 0xC3D2E1F0; context->count[0] = context->count[1] = 0; } static void SHA1_update(SHA1Context *context, const unsigned char *data, unsigned int len) { uint32_t i, j; j = (context->count[0] >> 3) & 63; if ((context->count[0] += len << 3) < (len << 3)) { context->count[1]++; } context->count[1] += (len >> 29); if ((j + len) > 63) { memcpy(&(context->buffer[j]), data, (i = 64 - j)); SHA1_transform(context->state, context->buffer); for ( ; (i + 63) < len; i += 64) { SHA1_transform(context->state, &(data[i])); } j = 0; } else { i = 0; } memcpy(&(context->buffer[j]), &(data[i]), len - i); } static void SHA1_final(unsigned char digest[20], SHA1Context *context) { uint32_t i; unsigned char finalcount[8]; for (i = 0; i < 8; i++) { finalcount[i] = (unsigned char) ((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255); } SHA1_update(context, (unsigned char *) "\200", 1); while ((context->count[0] & 504) != 448) { SHA1_update(context, (unsigned char *) "\0", 1); } SHA1_update(context, finalcount, 8); for (i = 0; i < 20; i++) { digest[i] = (unsigned char) ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); } memset(context->buffer, 0, 64); memset(context->state, 0, 20); memset(context->count, 0, 8); memset(&finalcount, 0, 8); SHA1_transform(context->state, context->buffer); } // HMAC-SHA-1: // // K - is key padded with zeros to 512 bits // m - is message // OPAD - 0x5c5c5c... // IPAD - 0x363636... // // HMAC(K,m) = SHA1((K ^ OPAD) . SHA1((K ^ IPAD) . m)) void HMAC_SHA1(unsigned char hmac[20], const unsigned char *key, int key_len, const unsigned char *message, int message_len) { unsigned char kopad[64], kipad[64]; int i; if (key_len > 64) { key_len = 64; } for (i = 0; i < key_len; i++) { kopad[i] = key[i] ^ 0x5c; kipad[i] = key[i] ^ 0x36; } for ( ; i < 64; i++) { kopad[i] = 0 ^ 0x5c; kipad[i] = 0 ^ 0x36; } unsigned char digest[20]; SHA1Context context; SHA1_init(&context); SHA1_update(&context, kipad, 64); SHA1_update(&context, message, message_len); SHA1_final(digest, &context); SHA1_init(&context); SHA1_update(&context, kopad, 64); SHA1_update(&context, digest, 20); SHA1_final(hmac, &context); } #define rot(x,k) (((x) << (k)) | ((x) >> (32 - (k)))) uint64_t hash(const unsigned char *k, int length) { uint32_t a, b, c; a = b = c = 0xdeadbeef + ((uint32_t) length); static uint32_t endianness_indicator = 0x1; if (((unsigned char *) &endianness_indicator)[0]) { while (length > 12) { a += k[0]; a += ((uint32_t) k[1]) << 8; a += ((uint32_t) k[2]) << 16; a += ((uint32_t) k[3]) << 24; b += k[4]; b += ((uint32_t) k[5]) << 8; b += ((uint32_t) k[6]) << 16; b += ((uint32_t) k[7]) << 24; c += k[8]; c += ((uint32_t) k[9]) << 8; c += ((uint32_t) k[10]) << 16; c += ((uint32_t) k[11]) << 24; a -= c; a ^= rot(c, 4); c += b; b -= a; b ^= rot(a, 6); a += c; c -= b; c ^= rot(b, 8); b += a; a -= c; a ^= rot(c, 16); c += b; b -= a; b ^= rot(a, 19); a += c; c -= b; c ^= rot(b, 4); b += a; length -= 12; k += 12; } switch(length) { case 12: c += ((uint32_t) k[11]) << 24; case 11: c += ((uint32_t) k[10]) << 16; case 10: c += ((uint32_t) k[9]) << 8; case 9 : c += k[8]; case 8 : b += ((uint32_t) k[7]) << 24; case 7 : b += ((uint32_t) k[6]) << 16; case 6 : b += ((uint32_t) k[5]) << 8; case 5 : b += k[4]; case 4 : a += ((uint32_t) k[3]) << 24; case 3 : a += ((uint32_t) k[2]) << 16; case 2 : a += ((uint32_t) k[1]) << 8; case 1 : a += k[0]; break; case 0 : goto end; } } else { while (length > 12) { a += ((uint32_t) k[0]) << 24; a += ((uint32_t) k[1]) << 16; a += ((uint32_t) k[2]) << 8; a += ((uint32_t) k[3]); b += ((uint32_t) k[4]) << 24; b += ((uint32_t) k[5]) << 16; b += ((uint32_t) k[6]) << 8; b += ((uint32_t) k[7]); c += ((uint32_t) k[8]) << 24; c += ((uint32_t) k[9]) << 16; c += ((uint32_t) k[10]) << 8; c += ((uint32_t) k[11]); a -= c; a ^= rot(c, 4); c += b; b -= a; b ^= rot(a, 6); a += c; c -= b; c ^= rot(b, 8); b += a; a -= c; a ^= rot(c, 16); c += b; b -= a; b ^= rot(a, 19); a += c; c -= b; c ^= rot(b, 4); b += a; length -= 12; k += 12; } switch(length) { case 12: c += k[11]; case 11: c += ((uint32_t) k[10]) << 8; case 10: c += ((uint32_t) k[9]) << 16; case 9 : c += ((uint32_t) k[8]) << 24; case 8 : b += k[7]; case 7 : b += ((uint32_t) k[6]) << 8; case 6 : b += ((uint32_t) k[5]) << 16; case 5 : b += ((uint32_t) k[4]) << 24; case 4 : a += k[3]; case 3 : a += ((uint32_t) k[2]) << 8; case 2 : a += ((uint32_t) k[1]) << 16; case 1 : a += ((uint32_t) k[0]) << 24; break; case 0 : goto end; } } c ^= b; c -= rot(b, 14); a ^= c; a -= rot(c, 11); b ^= a; b -= rot(a, 25); c ^= b; c -= rot(b, 16); a ^= c; a -= rot(c, 4); b ^= a; b -= rot(a, 14); c ^= b; c -= rot(b, 24); end: return ((((uint64_t) c) << 32) | b); } int is_blank(char c) { return ((c == ' ') || (c == '\t')); } libs3-2.0/src/s3.c0000644000175000001440000025146411640553711012351 0ustar bjiusers/** ************************************************************************** * s3.c * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ /** * This is a 'driver' program that simply converts command-line input into * calls to libs3 functions, and prints the results. **/ #include #include #include #include #include #include #include #include #include #include #include "libs3.h" // Some Windows stuff #ifndef FOPEN_EXTRA_FLAGS #define FOPEN_EXTRA_FLAGS "" #endif // Some Unix stuff (to work around Windows issues) #ifndef SLEEP_UNITS_PER_SECOND #define SLEEP_UNITS_PER_SECOND 1 #endif // Also needed for Windows, because somehow MinGW doesn't define this extern int putenv(char *); // Command-line options, saved as globals ------------------------------------ static int forceG = 0; static int showResponsePropertiesG = 0; static S3Protocol protocolG = S3ProtocolHTTPS; static S3UriStyle uriStyleG = S3UriStylePath; static int retriesG = 5; // Environment variables, saved as globals ---------------------------------- static const char *accessKeyIdG = 0; static const char *secretAccessKeyG = 0; // Request results, saved as globals ----------------------------------------- static int statusG = 0; static char errorDetailsG[4096] = { 0 }; // Other globals ------------------------------------------------------------- static char putenvBufG[256]; // Option prefixes ----------------------------------------------------------- #define LOCATION_PREFIX "location=" #define LOCATION_PREFIX_LEN (sizeof(LOCATION_PREFIX) - 1) #define CANNED_ACL_PREFIX "cannedAcl=" #define CANNED_ACL_PREFIX_LEN (sizeof(CANNED_ACL_PREFIX) - 1) #define PREFIX_PREFIX "prefix=" #define PREFIX_PREFIX_LEN (sizeof(PREFIX_PREFIX) - 1) #define MARKER_PREFIX "marker=" #define MARKER_PREFIX_LEN (sizeof(MARKER_PREFIX) - 1) #define DELIMITER_PREFIX "delimiter=" #define DELIMITER_PREFIX_LEN (sizeof(DELIMITER_PREFIX) - 1) #define MAXKEYS_PREFIX "maxkeys=" #define MAXKEYS_PREFIX_LEN (sizeof(MAXKEYS_PREFIX) - 1) #define FILENAME_PREFIX "filename=" #define FILENAME_PREFIX_LEN (sizeof(FILENAME_PREFIX) - 1) #define CONTENT_LENGTH_PREFIX "contentLength=" #define CONTENT_LENGTH_PREFIX_LEN (sizeof(CONTENT_LENGTH_PREFIX) - 1) #define CACHE_CONTROL_PREFIX "cacheControl=" #define CACHE_CONTROL_PREFIX_LEN (sizeof(CACHE_CONTROL_PREFIX) - 1) #define CONTENT_TYPE_PREFIX "contentType=" #define CONTENT_TYPE_PREFIX_LEN (sizeof(CONTENT_TYPE_PREFIX) - 1) #define MD5_PREFIX "md5=" #define MD5_PREFIX_LEN (sizeof(MD5_PREFIX) - 1) #define CONTENT_DISPOSITION_FILENAME_PREFIX "contentDispositionFilename=" #define CONTENT_DISPOSITION_FILENAME_PREFIX_LEN \ (sizeof(CONTENT_DISPOSITION_FILENAME_PREFIX) - 1) #define CONTENT_ENCODING_PREFIX "contentEncoding=" #define CONTENT_ENCODING_PREFIX_LEN (sizeof(CONTENT_ENCODING_PREFIX) - 1) #define EXPIRES_PREFIX "expires=" #define EXPIRES_PREFIX_LEN (sizeof(EXPIRES_PREFIX) - 1) #define X_AMZ_META_PREFIX "x-amz-meta-" #define X_AMZ_META_PREFIX_LEN (sizeof(X_AMZ_META_PREFIX) - 1) #define IF_MODIFIED_SINCE_PREFIX "ifModifiedSince=" #define IF_MODIFIED_SINCE_PREFIX_LEN (sizeof(IF_MODIFIED_SINCE_PREFIX) - 1) #define IF_NOT_MODIFIED_SINCE_PREFIX "ifNotmodifiedSince=" #define IF_NOT_MODIFIED_SINCE_PREFIX_LEN \ (sizeof(IF_NOT_MODIFIED_SINCE_PREFIX) - 1) #define IF_MATCH_PREFIX "ifMatch=" #define IF_MATCH_PREFIX_LEN (sizeof(IF_MATCH_PREFIX) - 1) #define IF_NOT_MATCH_PREFIX "ifNotMatch=" #define IF_NOT_MATCH_PREFIX_LEN (sizeof(IF_NOT_MATCH_PREFIX) - 1) #define START_BYTE_PREFIX "startByte=" #define START_BYTE_PREFIX_LEN (sizeof(START_BYTE_PREFIX) - 1) #define BYTE_COUNT_PREFIX "byteCount=" #define BYTE_COUNT_PREFIX_LEN (sizeof(BYTE_COUNT_PREFIX) - 1) #define ALL_DETAILS_PREFIX "allDetails=" #define ALL_DETAILS_PREFIX_LEN (sizeof(ALL_DETAILS_PREFIX) - 1) #define NO_STATUS_PREFIX "noStatus=" #define NO_STATUS_PREFIX_LEN (sizeof(NO_STATUS_PREFIX) - 1) #define RESOURCE_PREFIX "resource=" #define RESOURCE_PREFIX_LEN (sizeof(RESOURCE_PREFIX) - 1) #define TARGET_BUCKET_PREFIX "targetBucket=" #define TARGET_BUCKET_PREFIX_LEN (sizeof(TARGET_BUCKET_PREFIX) - 1) #define TARGET_PREFIX_PREFIX "targetPrefix=" #define TARGET_PREFIX_PREFIX_LEN (sizeof(TARGET_PREFIX_PREFIX) - 1) // util ---------------------------------------------------------------------- static void S3_init() { S3Status status; const char *hostname = getenv("S3_HOSTNAME"); if ((status = S3_initialize("s3", S3_INIT_ALL, hostname)) != S3StatusOK) { fprintf(stderr, "Failed to initialize libs3: %s\n", S3_get_status_name(status)); exit(-1); } } static void printError() { if (statusG < S3StatusErrorAccessDenied) { fprintf(stderr, "\nERROR: %s\n", S3_get_status_name(statusG)); } else { fprintf(stderr, "\nERROR: %s\n", S3_get_status_name(statusG)); fprintf(stderr, "%s\n", errorDetailsG); } } static void usageExit(FILE *out) { fprintf(out, "\n Options:\n" "\n" " Command Line:\n" "\n" " -f/--force : force operation despite warnings\n" " -h/--vhost-style : use virtual-host-style URIs (default is " "path-style)\n" " -u/--unencrypted : unencrypted (use HTTP instead of HTTPS)\n" " -s/--show-properties : show response properties on stdout\n" " -r/--retries : retry retryable failures this number of times\n" " (default is 5)\n" "\n" " Environment:\n" "\n" " S3_ACCESS_KEY_ID : S3 access key ID (required)\n" " S3_SECRET_ACCESS_KEY : S3 secret access key (required)\n" " S3_HOSTNAME : specify alternative S3 host (optional)\n" "\n" " Commands (with and [optional parameters]) :\n" "\n" " (NOTE: all command parameters take a value and are specified using the\n" " pattern parameter=value)\n" "\n" " help : Prints this help text\n" "\n" " list : Lists owned buckets\n" " [allDetails] : Show full details\n" "\n" " test : Tests a bucket for existence and accessibility\n" " : Bucket to test\n" "\n" " create : Create a new bucket\n" " : Bucket to create\n" " [cannedAcl] : Canned ACL for the bucket (see Canned ACLs)\n" " [location] : Location for bucket (for example, EU)\n" "\n" " delete : Delete a bucket or key\n" " [/] : Bucket or bucket/key to delete\n" "\n" " list : List bucket contents\n" " : Bucket to list\n" " [prefix] : Prefix for results set\n" " [marker] : Where in results set to start listing\n" " [delimiter] : Delimiter for rolling up results set\n" " [maxkeys] : Maximum number of keys to return in results set\n" " [allDetails] : Show full details for each key\n" "\n" " getacl : Get the ACL of a bucket or key\n" " [/] : Bucket or bucket/key to get the ACL of\n" " [filename] : Output filename for ACL (default is stdout)\n" "\n" " setacl : Set the ACL of a bucket or key\n" " [/] : Bucket or bucket/key to set the ACL of\n" " [filename] : Input filename for ACL (default is stdin)\n" "\n" " getlogging : Get the logging status of a bucket\n" " : Bucket to get the logging status of\n" " [filename] : Output filename for ACL (default is stdout)\n" "\n" " setlogging : Set the logging status of a bucket\n" " : Bucket to set the logging status of\n" " [targetBucket] : Target bucket to log to; if not present, disables\n" " logging\n" " [targetPrefix] : Key prefix to use for logs\n" " [filename] : Input filename for ACL (default is stdin)\n" "\n" " put : Puts an object\n" " / : Bucket/key to put object to\n" " [filename] : Filename to read source data from " "(default is stdin)\n" " [contentLength] : How many bytes of source data to put (required if\n" " source file is stdin)\n" " [cacheControl] : Cache-Control HTTP header string to associate with\n" " object\n" " [contentType] : Content-Type HTTP header string to associate with\n" " object\n" " [md5] : MD5 for validating source data\n" " [contentDispositionFilename] : Content-Disposition filename string to\n" " associate with object\n" " [contentEncoding] : Content-Encoding HTTP header string to associate\n" " with object\n" " [expires] : Expiration date to associate with object\n" " [cannedAcl] : Canned ACL for the object (see Canned ACLs)\n" " [x-amz-meta-...]] : Metadata headers to associate with the object\n" "\n" " copy : Copies an object; if any options are set, the " "entire\n" " metadata of the object is replaced\n" " / : Source bucket/key\n" " / : Destination bucket/key\n" " [cacheControl] : Cache-Control HTTP header string to associate with\n" " object\n" " [contentType] : Content-Type HTTP header string to associate with\n" " object\n" " [contentDispositionFilename] : Content-Disposition filename string to\n" " associate with object\n" " [contentEncoding] : Content-Encoding HTTP header string to associate\n" " with object\n" " [expires] : Expiration date to associate with object\n" " [cannedAcl] : Canned ACL for the object (see Canned ACLs)\n" " [x-amz-meta-...]] : Metadata headers to associate with the object\n" "\n" " get : Gets an object\n" " / : Bucket/key of object to get\n" " [filename] : Filename to write object data to (required if -s\n" " command line parameter was used)\n" " [ifModifiedSince] : Only return the object if it has been modified " "since\n" " this date\n" " [ifNotmodifiedSince] : Only return the object if it has not been " "modified\n" " since this date\n" " [ifMatch] : Only return the object if its ETag header matches\n" " this string\n" " [ifNotMatch] : Only return the object if its ETag header does " "not\n" " match this string\n" " [startByte] : First byte of byte range to return\n" " [byteCount] : Number of bytes of byte range to return\n" "\n" " head : Gets only the headers of an object, implies -s\n" " / : Bucket/key of object to get headers of\n" "\n" " gqs : Generates an authenticated query string\n" " [/] : Bucket or bucket/key to generate query string for\n" " [expires] : Expiration date for query string\n" " [resource] : Sub-resource of key for query string, without a\n" " leading '?', for example, \"torrent\"\n" "\n" " Canned ACLs:\n" "\n" " The following canned ACLs are supported:\n" " private (default), public-read, public-read-write, authenticated-read\n" "\n" " ACL Format:\n" "\n" " For the getacl and setacl commands, the format of the ACL list is:\n" " 1) An initial line giving the owner id in this format:\n" " OwnerID \n" " 2) Optional header lines, giving column headers, starting with the\n" " word \"Type\", or with some number of dashes\n" " 3) Grant lines, of the form:\n" " (whitespace) (whitespace) \n" " where Grant Type is one of: Email, UserID, or Group, and\n" " Grantee is the identification of the grantee based on this type,\n" " and Permission is one of: READ, WRITE, READ_ACP, or FULL_CONTROL.\n" "\n" " Note that the easiest way to modify an ACL is to first get it, saving it\n" " into a file, then modifying the file, and then setting the modified file\n" " back as the new ACL for the bucket/object.\n" "\n" " Date Format:\n" "\n" " The format for dates used in parameters is as ISO 8601 dates, i.e.\n" " YYYY-MM-DDTHH:MM:SS[+/-dd:dd]. Examples:\n" " 2008-07-29T20:36:14\n" " 2008-07-29T20:36:14-06:00\n" " 2008-07-29T20:36:14+11:30\n" "\n"); exit(-1); } static uint64_t convertInt(const char *str, const char *paramName) { uint64_t ret = 0; while (*str) { if (!isdigit(*str)) { fprintf(stderr, "\nERROR: Nondigit in %s parameter: %c\n", paramName, *str); usageExit(stderr); } ret *= 10; ret += (*str++ - '0'); } return ret; } typedef struct growbuffer { // The total number of bytes, and the start byte int size; // The start byte int start; // The blocks char data[64 * 1024]; struct growbuffer *prev, *next; } growbuffer; // returns nonzero on success, zero on out of memory static int growbuffer_append(growbuffer **gb, const char *data, int dataLen) { while (dataLen) { growbuffer *buf = *gb ? (*gb)->prev : 0; if (!buf || (buf->size == sizeof(buf->data))) { buf = (growbuffer *) malloc(sizeof(growbuffer)); if (!buf) { return 0; } buf->size = 0; buf->start = 0; if (*gb) { buf->prev = (*gb)->prev; buf->next = *gb; (*gb)->prev->next = buf; (*gb)->prev = buf; } else { buf->prev = buf->next = buf; *gb = buf; } } int toCopy = (sizeof(buf->data) - buf->size); if (toCopy > dataLen) { toCopy = dataLen; } memcpy(&(buf->data[buf->size]), data, toCopy); buf->size += toCopy, data += toCopy, dataLen -= toCopy; } return 1; } static void growbuffer_read(growbuffer **gb, int amt, int *amtReturn, char *buffer) { *amtReturn = 0; growbuffer *buf = *gb; if (!buf) { return; } *amtReturn = (buf->size > amt) ? amt : buf->size; memcpy(buffer, &(buf->data[buf->start]), *amtReturn); buf->start += *amtReturn, buf->size -= *amtReturn; if (buf->size == 0) { if (buf->next == buf) { *gb = 0; } else { *gb = buf->next; buf->prev->next = buf->next; buf->next->prev = buf->prev; } free(buf); } } static void growbuffer_destroy(growbuffer *gb) { growbuffer *start = gb; while (gb) { growbuffer *next = gb->next; free(gb); gb = (next == start) ? 0 : next; } } // Convenience utility for making the code look nicer. Tests a string // against a format; only the characters specified in the format are // checked (i.e. if the string is longer than the format, the string still // checks out ok). Format characters are: // d - is a digit // anything else - is that character // Returns nonzero the string checks out, zero if it does not. static int checkString(const char *str, const char *format) { while (*format) { if (*format == 'd') { if (!isdigit(*str)) { return 0; } } else if (*str != *format) { return 0; } str++, format++; } return 1; } static int64_t parseIso8601Time(const char *str) { // Check to make sure that it has a valid format if (!checkString(str, "dddd-dd-ddTdd:dd:dd")) { return -1; } #define nextnum() (((*str - '0') * 10) + (*(str + 1) - '0')) // Convert it struct tm stm; memset(&stm, 0, sizeof(stm)); stm.tm_year = (nextnum() - 19) * 100; str += 2; stm.tm_year += nextnum(); str += 3; stm.tm_mon = nextnum() - 1; str += 3; stm.tm_mday = nextnum(); str += 3; stm.tm_hour = nextnum(); str += 3; stm.tm_min = nextnum(); str += 3; stm.tm_sec = nextnum(); str += 2; stm.tm_isdst = -1; // This is hokey but it's the recommended way ... char *tz = getenv("TZ"); snprintf(putenvBufG, sizeof(putenvBufG), "TZ=UTC"); putenv(putenvBufG); int64_t ret = mktime(&stm); snprintf(putenvBufG, sizeof(putenvBufG), "TZ=%s", tz ? tz : ""); putenv(putenvBufG); // Skip the millis if (*str == '.') { str++; while (isdigit(*str)) { str++; } } if (checkString(str, "-dd:dd") || checkString(str, "+dd:dd")) { int sign = (*str++ == '-') ? -1 : 1; int hours = nextnum(); str += 3; int minutes = nextnum(); ret += (-sign * (((hours * 60) + minutes) * 60)); } // Else it should be Z to be a conformant time string, but we just assume // that it is rather than enforcing that return ret; } // Simple ACL format: Lines of this format: // Type - ignored // Starting with a dash - ignored // Email email_address permission // UserID user_id (display_name) permission // Group Authenticated AWS Users permission // Group All Users permission // permission is one of READ, WRITE, READ_ACP, WRITE_ACP, FULL_CONTROL static int convert_simple_acl(char *aclXml, char *ownerId, char *ownerDisplayName, int *aclGrantCountReturn, S3AclGrant *aclGrants) { *aclGrantCountReturn = 0; *ownerId = 0; *ownerDisplayName = 0; #define SKIP_SPACE(require_more) \ do { \ while (isspace(*aclXml)) { \ aclXml++; \ } \ if (require_more && !*aclXml) { \ return 0; \ } \ } while (0) #define COPY_STRING_MAXLEN(field, maxlen) \ do { \ SKIP_SPACE(1); \ int len = 0; \ while ((len < maxlen) && !isspace(*aclXml)) { \ field[len++] = *aclXml++; \ } \ field[len] = 0; \ } while (0) #define COPY_STRING(field) \ COPY_STRING_MAXLEN(field, (int) (sizeof(field) - 1)) while (1) { SKIP_SPACE(0); if (!*aclXml) { break; } // Skip Type lines and dash lines if (!strncmp(aclXml, "Type", sizeof("Type") - 1) || (*aclXml == '-')) { while (*aclXml && ((*aclXml != '\n') && (*aclXml != '\r'))) { aclXml++; } continue; } if (!strncmp(aclXml, "OwnerID", sizeof("OwnerID") - 1)) { aclXml += sizeof("OwnerID") - 1; COPY_STRING_MAXLEN(ownerId, S3_MAX_GRANTEE_USER_ID_SIZE); SKIP_SPACE(1); COPY_STRING_MAXLEN(ownerDisplayName, S3_MAX_GRANTEE_DISPLAY_NAME_SIZE); continue; } if (*aclGrantCountReturn == S3_MAX_ACL_GRANT_COUNT) { return 0; } S3AclGrant *grant = &(aclGrants[(*aclGrantCountReturn)++]); if (!strncmp(aclXml, "Email", sizeof("Email") - 1)) { grant->granteeType = S3GranteeTypeAmazonCustomerByEmail; aclXml += sizeof("Email") - 1; COPY_STRING(grant->grantee.amazonCustomerByEmail.emailAddress); } else if (!strncmp(aclXml, "UserID", sizeof("UserID") - 1)) { grant->granteeType = S3GranteeTypeCanonicalUser; aclXml += sizeof("UserID") - 1; COPY_STRING(grant->grantee.canonicalUser.id); SKIP_SPACE(1); // Now do display name COPY_STRING(grant->grantee.canonicalUser.displayName); } else if (!strncmp(aclXml, "Group", sizeof("Group") - 1)) { aclXml += sizeof("Group") - 1; SKIP_SPACE(1); if (!strncmp(aclXml, "Authenticated AWS Users", sizeof("Authenticated AWS Users") - 1)) { grant->granteeType = S3GranteeTypeAllAwsUsers; aclXml += (sizeof("Authenticated AWS Users") - 1); } else if (!strncmp(aclXml, "All Users", sizeof("All Users") - 1)) { grant->granteeType = S3GranteeTypeAllUsers; aclXml += (sizeof("All Users") - 1); } else if (!strncmp(aclXml, "Log Delivery", sizeof("Log Delivery") - 1)) { grant->granteeType = S3GranteeTypeLogDelivery; aclXml += (sizeof("Log Delivery") - 1); } else { return 0; } } else { return 0; } SKIP_SPACE(1); if (!strncmp(aclXml, "READ_ACP", sizeof("READ_ACP") - 1)) { grant->permission = S3PermissionReadACP; aclXml += (sizeof("READ_ACP") - 1); } else if (!strncmp(aclXml, "READ", sizeof("READ") - 1)) { grant->permission = S3PermissionRead; aclXml += (sizeof("READ") - 1); } else if (!strncmp(aclXml, "WRITE_ACP", sizeof("WRITE_ACP") - 1)) { grant->permission = S3PermissionWriteACP; aclXml += (sizeof("WRITE_ACP") - 1); } else if (!strncmp(aclXml, "WRITE", sizeof("WRITE") - 1)) { grant->permission = S3PermissionWrite; aclXml += (sizeof("WRITE") - 1); } else if (!strncmp(aclXml, "FULL_CONTROL", sizeof("FULL_CONTROL") - 1)) { grant->permission = S3PermissionFullControl; aclXml += (sizeof("FULL_CONTROL") - 1); } } return 1; } static int should_retry() { if (retriesG--) { // Sleep before next retry; start out with a 1 second sleep static int retrySleepInterval = 1 * SLEEP_UNITS_PER_SECOND; sleep(retrySleepInterval); // Next sleep 1 second longer retrySleepInterval++; return 1; } return 0; } static struct option longOptionsG[] = { { "force", no_argument, 0, 'f' }, { "vhost-style", no_argument, 0, 'h' }, { "unencrypted", no_argument, 0, 'u' }, { "show-properties", no_argument, 0, 's' }, { "retries", required_argument, 0, 'r' }, { 0, 0, 0, 0 } }; // response properties callback ---------------------------------------------- // This callback does the same thing for every request type: prints out the // properties if the user has requested them to be so static S3Status responsePropertiesCallback (const S3ResponseProperties *properties, void *callbackData) { (void) callbackData; if (!showResponsePropertiesG) { return S3StatusOK; } #define print_nonnull(name, field) \ do { \ if (properties-> field) { \ printf("%s: %s\n", name, properties-> field); \ } \ } while (0) print_nonnull("Content-Type", contentType); print_nonnull("Request-Id", requestId); print_nonnull("Request-Id-2", requestId2); if (properties->contentLength > 0) { printf("Content-Length: %lld\n", (unsigned long long) properties->contentLength); } print_nonnull("Server", server); print_nonnull("ETag", eTag); if (properties->lastModified > 0) { char timebuf[256]; time_t t = (time_t) properties->lastModified; // gmtime is not thread-safe but we don't care here. strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%SZ", gmtime(&t)); printf("Last-Modified: %s\n", timebuf); } int i; for (i = 0; i < properties->metaDataCount; i++) { printf("x-amz-meta-%s: %s\n", properties->metaData[i].name, properties->metaData[i].value); } return S3StatusOK; } // response complete callback ------------------------------------------------ // This callback does the same thing for every request type: saves the status // and error stuff in global variables static void responseCompleteCallback(S3Status status, const S3ErrorDetails *error, void *callbackData) { (void) callbackData; statusG = status; // Compose the error details message now, although we might not use it. // Can't just save a pointer to [error] since it's not guaranteed to last // beyond this callback int len = 0; if (error && error->message) { len += snprintf(&(errorDetailsG[len]), sizeof(errorDetailsG) - len, " Message: %s\n", error->message); } if (error && error->resource) { len += snprintf(&(errorDetailsG[len]), sizeof(errorDetailsG) - len, " Resource: %s\n", error->resource); } if (error && error->furtherDetails) { len += snprintf(&(errorDetailsG[len]), sizeof(errorDetailsG) - len, " Further Details: %s\n", error->furtherDetails); } if (error && error->extraDetailsCount) { len += snprintf(&(errorDetailsG[len]), sizeof(errorDetailsG) - len, "%s", " Extra Details:\n"); int i; for (i = 0; i < error->extraDetailsCount; i++) { len += snprintf(&(errorDetailsG[len]), sizeof(errorDetailsG) - len, " %s: %s\n", error->extraDetails[i].name, error->extraDetails[i].value); } } } // list service -------------------------------------------------------------- typedef struct list_service_data { int headerPrinted; int allDetails; } list_service_data; static void printListServiceHeader(int allDetails) { printf("%-56s %-20s", " Bucket", " Created"); if (allDetails) { printf(" %-64s %-12s", " Owner ID", "Display Name"); } printf("\n"); printf("-------------------------------------------------------- " "--------------------"); if (allDetails) { printf(" -------------------------------------------------" "--------------- ------------"); } printf("\n"); } static S3Status listServiceCallback(const char *ownerId, const char *ownerDisplayName, const char *bucketName, int64_t creationDate, void *callbackData) { list_service_data *data = (list_service_data *) callbackData; if (!data->headerPrinted) { data->headerPrinted = 1; printListServiceHeader(data->allDetails); } char timebuf[256]; if (creationDate >= 0) { time_t t = (time_t) creationDate; strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%SZ", gmtime(&t)); } else { timebuf[0] = 0; } printf("%-56s %-20s", bucketName, timebuf); if (data->allDetails) { printf(" %-64s %-12s", ownerId ? ownerId : "", ownerDisplayName ? ownerDisplayName : ""); } printf("\n"); return S3StatusOK; } static void list_service(int allDetails) { list_service_data data; data.headerPrinted = 0; data.allDetails = allDetails; S3_init(); S3ListServiceHandler listServiceHandler = { { &responsePropertiesCallback, &responseCompleteCallback }, &listServiceCallback }; do { S3_list_service(protocolG, accessKeyIdG, secretAccessKeyG, 0, 0, &listServiceHandler, &data); } while (S3_status_is_retryable(statusG) && should_retry()); if (statusG == S3StatusOK) { if (!data.headerPrinted) { printListServiceHeader(allDetails); } } else { printError(); } S3_deinitialize(); } // test bucket --------------------------------------------------------------- static void test_bucket(int argc, char **argv, int optindex) { // test bucket if (optindex == argc) { fprintf(stderr, "\nERROR: Missing parameter: bucket\n"); usageExit(stderr); } const char *bucketName = argv[optindex++]; if (optindex != argc) { fprintf(stderr, "\nERROR: Extraneous parameter: %s\n", argv[optindex]); usageExit(stderr); } S3_init(); S3ResponseHandler responseHandler = { &responsePropertiesCallback, &responseCompleteCallback }; char locationConstraint[64]; do { S3_test_bucket(protocolG, uriStyleG, accessKeyIdG, secretAccessKeyG, 0, bucketName, sizeof(locationConstraint), locationConstraint, 0, &responseHandler, 0); } while (S3_status_is_retryable(statusG) && should_retry()); const char *result; switch (statusG) { case S3StatusOK: // bucket exists result = locationConstraint[0] ? locationConstraint : "USA"; break; case S3StatusErrorNoSuchBucket: result = "Does Not Exist"; break; case S3StatusErrorAccessDenied: result = "Access Denied"; break; default: result = 0; break; } if (result) { printf("%-56s %-20s\n", " Bucket", " Status"); printf("-------------------------------------------------------- " "--------------------\n"); printf("%-56s %-20s\n", bucketName, result); } else { printError(); } S3_deinitialize(); } // create bucket ------------------------------------------------------------- static void create_bucket(int argc, char **argv, int optindex) { if (optindex == argc) { fprintf(stderr, "\nERROR: Missing parameter: bucket\n"); usageExit(stderr); } const char *bucketName = argv[optindex++]; if (!forceG && (S3_validate_bucket_name (bucketName, S3UriStyleVirtualHost) != S3StatusOK)) { fprintf(stderr, "\nWARNING: Bucket name is not valid for " "virtual-host style URI access.\n"); fprintf(stderr, "Bucket not created. Use -f option to force the " "bucket to be created despite\n"); fprintf(stderr, "this warning.\n\n"); exit(-1); } const char *locationConstraint = 0; S3CannedAcl cannedAcl = S3CannedAclPrivate; while (optindex < argc) { char *param = argv[optindex++]; if (!strncmp(param, LOCATION_PREFIX, LOCATION_PREFIX_LEN)) { locationConstraint = &(param[LOCATION_PREFIX_LEN]); } else if (!strncmp(param, CANNED_ACL_PREFIX, CANNED_ACL_PREFIX_LEN)) { char *val = &(param[CANNED_ACL_PREFIX_LEN]); if (!strcmp(val, "private")) { cannedAcl = S3CannedAclPrivate; } else if (!strcmp(val, "public-read")) { cannedAcl = S3CannedAclPublicRead; } else if (!strcmp(val, "public-read-write")) { cannedAcl = S3CannedAclPublicReadWrite; } else if (!strcmp(val, "authenticated-read")) { cannedAcl = S3CannedAclAuthenticatedRead; } else { fprintf(stderr, "\nERROR: Unknown canned ACL: %s\n", val); usageExit(stderr); } } else { fprintf(stderr, "\nERROR: Unknown param: %s\n", param); usageExit(stderr); } } S3_init(); S3ResponseHandler responseHandler = { &responsePropertiesCallback, &responseCompleteCallback }; do { S3_create_bucket(protocolG, accessKeyIdG, secretAccessKeyG, 0, bucketName, cannedAcl, locationConstraint, 0, &responseHandler, 0); } while (S3_status_is_retryable(statusG) && should_retry()); if (statusG == S3StatusOK) { printf("Bucket successfully created.\n"); } else { printError(); } S3_deinitialize(); } // delete bucket ------------------------------------------------------------- static void delete_bucket(int argc, char **argv, int optindex) { if (optindex == argc) { fprintf(stderr, "\nERROR: Missing parameter: bucket\n"); usageExit(stderr); } const char *bucketName = argv[optindex++]; if (optindex != argc) { fprintf(stderr, "\nERROR: Extraneous parameter: %s\n", argv[optindex]); usageExit(stderr); } S3_init(); S3ResponseHandler responseHandler = { &responsePropertiesCallback, &responseCompleteCallback }; do { S3_delete_bucket(protocolG, uriStyleG, accessKeyIdG, secretAccessKeyG, 0, bucketName, 0, &responseHandler, 0); } while (S3_status_is_retryable(statusG) && should_retry()); if (statusG != S3StatusOK) { printError(); } S3_deinitialize(); } // list bucket --------------------------------------------------------------- typedef struct list_bucket_callback_data { int isTruncated; char nextMarker[1024]; int keyCount; int allDetails; } list_bucket_callback_data; static void printListBucketHeader(int allDetails) { printf("%-50s %-20s %-5s", " Key", " Last Modified", "Size"); if (allDetails) { printf(" %-34s %-64s %-12s", " ETag", " Owner ID", "Display Name"); } printf("\n"); printf("-------------------------------------------------- " "-------------------- -----"); if (allDetails) { printf(" ---------------------------------- " "-------------------------------------------------" "--------------- ------------"); } printf("\n"); } static S3Status listBucketCallback(int isTruncated, const char *nextMarker, int contentsCount, const S3ListBucketContent *contents, int commonPrefixesCount, const char **commonPrefixes, void *callbackData) { list_bucket_callback_data *data = (list_bucket_callback_data *) callbackData; data->isTruncated = isTruncated; // This is tricky. S3 doesn't return the NextMarker if there is no // delimiter. Why, I don't know, since it's still useful for paging // through results. We want NextMarker to be the last content in the // list, so set it to that if necessary. if ((!nextMarker || !nextMarker[0]) && contentsCount) { nextMarker = contents[contentsCount - 1].key; } if (nextMarker) { snprintf(data->nextMarker, sizeof(data->nextMarker), "%s", nextMarker); } else { data->nextMarker[0] = 0; } if (contentsCount && !data->keyCount) { printListBucketHeader(data->allDetails); } int i; for (i = 0; i < contentsCount; i++) { const S3ListBucketContent *content = &(contents[i]); char timebuf[256]; if (0) { time_t t = (time_t) content->lastModified; strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%SZ", gmtime(&t)); printf("\nKey: %s\n", content->key); printf("Last Modified: %s\n", timebuf); printf("ETag: %s\n", content->eTag); printf("Size: %llu\n", (unsigned long long) content->size); if (content->ownerId) { printf("Owner ID: %s\n", content->ownerId); } if (content->ownerDisplayName) { printf("Owner Display Name: %s\n", content->ownerDisplayName); } } else { time_t t = (time_t) content->lastModified; strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%SZ", gmtime(&t)); char sizebuf[16]; if (content->size < 100000) { sprintf(sizebuf, "%5llu", (unsigned long long) content->size); } else if (content->size < (1024 * 1024)) { sprintf(sizebuf, "%4lluK", ((unsigned long long) content->size) / 1024ULL); } else if (content->size < (10 * 1024 * 1024)) { float f = content->size; f /= (1024 * 1024); sprintf(sizebuf, "%1.2fM", f); } else if (content->size < (1024 * 1024 * 1024)) { sprintf(sizebuf, "%4lluM", ((unsigned long long) content->size) / (1024ULL * 1024ULL)); } else { float f = (content->size / 1024); f /= (1024 * 1024); sprintf(sizebuf, "%1.2fG", f); } printf("%-50s %s %s", content->key, timebuf, sizebuf); if (data->allDetails) { printf(" %-34s %-64s %-12s", content->eTag, content->ownerId ? content->ownerId : "", content->ownerDisplayName ? content->ownerDisplayName : ""); } printf("\n"); } } data->keyCount += contentsCount; for (i = 0; i < commonPrefixesCount; i++) { printf("\nCommon Prefix: %s\n", commonPrefixes[i]); } return S3StatusOK; } static void list_bucket(const char *bucketName, const char *prefix, const char *marker, const char *delimiter, int maxkeys, int allDetails) { S3_init(); S3BucketContext bucketContext = { 0, bucketName, protocolG, uriStyleG, accessKeyIdG, secretAccessKeyG }; S3ListBucketHandler listBucketHandler = { { &responsePropertiesCallback, &responseCompleteCallback }, &listBucketCallback }; list_bucket_callback_data data; snprintf(data.nextMarker, sizeof(data.nextMarker), "%s", marker); data.keyCount = 0; data.allDetails = allDetails; do { data.isTruncated = 0; do { S3_list_bucket(&bucketContext, prefix, data.nextMarker, delimiter, maxkeys, 0, &listBucketHandler, &data); } while (S3_status_is_retryable(statusG) && should_retry()); if (statusG != S3StatusOK) { break; } } while (data.isTruncated && (!maxkeys || (data.keyCount < maxkeys))); if (statusG == S3StatusOK) { if (!data.keyCount) { printListBucketHeader(allDetails); } } else { printError(); } S3_deinitialize(); } static void list(int argc, char **argv, int optindex) { if (optindex == argc) { list_service(0); return; } const char *bucketName = 0; const char *prefix = 0, *marker = 0, *delimiter = 0; int maxkeys = 0, allDetails = 0; while (optindex < argc) { char *param = argv[optindex++]; if (!strncmp(param, PREFIX_PREFIX, PREFIX_PREFIX_LEN)) { prefix = &(param[PREFIX_PREFIX_LEN]); } else if (!strncmp(param, MARKER_PREFIX, MARKER_PREFIX_LEN)) { marker = &(param[MARKER_PREFIX_LEN]); } else if (!strncmp(param, DELIMITER_PREFIX, DELIMITER_PREFIX_LEN)) { delimiter = &(param[DELIMITER_PREFIX_LEN]); } else if (!strncmp(param, MAXKEYS_PREFIX, MAXKEYS_PREFIX_LEN)) { maxkeys = convertInt(&(param[MAXKEYS_PREFIX_LEN]), "maxkeys"); } else if (!strncmp(param, ALL_DETAILS_PREFIX, ALL_DETAILS_PREFIX_LEN)) { const char *ad = &(param[ALL_DETAILS_PREFIX_LEN]); if (!strcmp(ad, "true") || !strcmp(ad, "TRUE") || !strcmp(ad, "yes") || !strcmp(ad, "YES") || !strcmp(ad, "1")) { allDetails = 1; } } else if (!bucketName) { bucketName = param; } else { fprintf(stderr, "\nERROR: Unknown param: %s\n", param); usageExit(stderr); } } if (bucketName) { list_bucket(bucketName, prefix, marker, delimiter, maxkeys, allDetails); } else { list_service(allDetails); } } // delete object ------------------------------------------------------------- static void delete_object(int argc, char **argv, int optindex) { (void) argc; // Split bucket/key char *slash = argv[optindex]; // We know there is a slash in there, put_object is only called if so while (*slash && (*slash != '/')) { slash++; } *slash++ = 0; const char *bucketName = argv[optindex++]; const char *key = slash; S3_init(); S3BucketContext bucketContext = { 0, bucketName, protocolG, uriStyleG, accessKeyIdG, secretAccessKeyG }; S3ResponseHandler responseHandler = { 0, &responseCompleteCallback }; do { S3_delete_object(&bucketContext, key, 0, &responseHandler, 0); } while (S3_status_is_retryable(statusG) && should_retry()); if ((statusG != S3StatusOK) && (statusG != S3StatusErrorPreconditionFailed)) { printError(); } S3_deinitialize(); } // put object ---------------------------------------------------------------- typedef struct put_object_callback_data { FILE *infile; growbuffer *gb; uint64_t contentLength, originalContentLength; int noStatus; } put_object_callback_data; static int putObjectDataCallback(int bufferSize, char *buffer, void *callbackData) { put_object_callback_data *data = (put_object_callback_data *) callbackData; int ret = 0; if (data->contentLength) { int toRead = ((data->contentLength > (unsigned) bufferSize) ? (unsigned) bufferSize : data->contentLength); if (data->gb) { growbuffer_read(&(data->gb), toRead, &ret, buffer); } else if (data->infile) { ret = fread(buffer, 1, toRead, data->infile); } } data->contentLength -= ret; if (data->contentLength && !data->noStatus) { // Avoid a weird bug in MingW, which won't print the second integer // value properly when it's in the same call, so print separately printf("%llu bytes remaining ", (unsigned long long) data->contentLength); printf("(%d%% complete) ...\n", (int) (((data->originalContentLength - data->contentLength) * 100) / data->originalContentLength)); } return ret; } static void put_object(int argc, char **argv, int optindex) { if (optindex == argc) { fprintf(stderr, "\nERROR: Missing parameter: bucket/key\n"); usageExit(stderr); } // Split bucket/key char *slash = argv[optindex]; while (*slash && (*slash != '/')) { slash++; } if (!*slash || !*(slash + 1)) { fprintf(stderr, "\nERROR: Invalid bucket/key name: %s\n", argv[optindex]); usageExit(stderr); } *slash++ = 0; const char *bucketName = argv[optindex++]; const char *key = slash; const char *filename = 0; uint64_t contentLength = 0; const char *cacheControl = 0, *contentType = 0, *md5 = 0; const char *contentDispositionFilename = 0, *contentEncoding = 0; int64_t expires = -1; S3CannedAcl cannedAcl = S3CannedAclPrivate; int metaPropertiesCount = 0; S3NameValue metaProperties[S3_MAX_METADATA_COUNT]; int noStatus = 0; while (optindex < argc) { char *param = argv[optindex++]; if (!strncmp(param, FILENAME_PREFIX, FILENAME_PREFIX_LEN)) { filename = &(param[FILENAME_PREFIX_LEN]); } else if (!strncmp(param, CONTENT_LENGTH_PREFIX, CONTENT_LENGTH_PREFIX_LEN)) { contentLength = convertInt(&(param[CONTENT_LENGTH_PREFIX_LEN]), "contentLength"); if (contentLength > (5LL * 1024 * 1024 * 1024)) { fprintf(stderr, "\nERROR: contentLength must be no greater " "than 5 GB\n"); usageExit(stderr); } } else if (!strncmp(param, CACHE_CONTROL_PREFIX, CACHE_CONTROL_PREFIX_LEN)) { cacheControl = &(param[CACHE_CONTROL_PREFIX_LEN]); } else if (!strncmp(param, CONTENT_TYPE_PREFIX, CONTENT_TYPE_PREFIX_LEN)) { contentType = &(param[CONTENT_TYPE_PREFIX_LEN]); } else if (!strncmp(param, MD5_PREFIX, MD5_PREFIX_LEN)) { md5 = &(param[MD5_PREFIX_LEN]); } else if (!strncmp(param, CONTENT_DISPOSITION_FILENAME_PREFIX, CONTENT_DISPOSITION_FILENAME_PREFIX_LEN)) { contentDispositionFilename = &(param[CONTENT_DISPOSITION_FILENAME_PREFIX_LEN]); } else if (!strncmp(param, CONTENT_ENCODING_PREFIX, CONTENT_ENCODING_PREFIX_LEN)) { contentEncoding = &(param[CONTENT_ENCODING_PREFIX_LEN]); } else if (!strncmp(param, EXPIRES_PREFIX, EXPIRES_PREFIX_LEN)) { expires = parseIso8601Time(&(param[EXPIRES_PREFIX_LEN])); if (expires < 0) { fprintf(stderr, "\nERROR: Invalid expires time " "value; ISO 8601 time format required\n"); usageExit(stderr); } } else if (!strncmp(param, X_AMZ_META_PREFIX, X_AMZ_META_PREFIX_LEN)) { if (metaPropertiesCount == S3_MAX_METADATA_COUNT) { fprintf(stderr, "\nERROR: Too many x-amz-meta- properties, " "limit %lu: %s\n", (unsigned long) S3_MAX_METADATA_COUNT, param); usageExit(stderr); } char *name = &(param[X_AMZ_META_PREFIX_LEN]); char *value = name; while (*value && (*value != '=')) { value++; } if (!*value || !*(value + 1)) { fprintf(stderr, "\nERROR: Invalid parameter: %s\n", param); usageExit(stderr); } *value++ = 0; metaProperties[metaPropertiesCount].name = name; metaProperties[metaPropertiesCount++].value = value; } else if (!strncmp(param, CANNED_ACL_PREFIX, CANNED_ACL_PREFIX_LEN)) { char *val = &(param[CANNED_ACL_PREFIX_LEN]); if (!strcmp(val, "private")) { cannedAcl = S3CannedAclPrivate; } else if (!strcmp(val, "public-read")) { cannedAcl = S3CannedAclPublicRead; } else if (!strcmp(val, "public-read-write")) { cannedAcl = S3CannedAclPublicReadWrite; } else if (!strcmp(val, "authenticated-read")) { cannedAcl = S3CannedAclAuthenticatedRead; } else { fprintf(stderr, "\nERROR: Unknown canned ACL: %s\n", val); usageExit(stderr); } } else if (!strncmp(param, NO_STATUS_PREFIX, NO_STATUS_PREFIX_LEN)) { const char *ns = &(param[NO_STATUS_PREFIX_LEN]); if (!strcmp(ns, "true") || !strcmp(ns, "TRUE") || !strcmp(ns, "yes") || !strcmp(ns, "YES") || !strcmp(ns, "1")) { noStatus = 1; } } else { fprintf(stderr, "\nERROR: Unknown param: %s\n", param); usageExit(stderr); } } put_object_callback_data data; data.infile = 0; data.gb = 0; data.noStatus = noStatus; if (filename) { if (!contentLength) { struct stat statbuf; // Stat the file to get its length if (stat(filename, &statbuf) == -1) { fprintf(stderr, "\nERROR: Failed to stat file %s: ", filename); perror(0); exit(-1); } contentLength = statbuf.st_size; } // Open the file if (!(data.infile = fopen(filename, "r" FOPEN_EXTRA_FLAGS))) { fprintf(stderr, "\nERROR: Failed to open input file %s: ", filename); perror(0); exit(-1); } } else { // Read from stdin. If contentLength is not provided, we have // to read it all in to get contentLength. if (!contentLength) { // Read all if stdin to get the data char buffer[64 * 1024]; while (1) { int amtRead = fread(buffer, 1, sizeof(buffer), stdin); if (amtRead == 0) { break; } if (!growbuffer_append(&(data.gb), buffer, amtRead)) { fprintf(stderr, "\nERROR: Out of memory while reading " "stdin\n"); exit(-1); } contentLength += amtRead; if (amtRead < (int) sizeof(buffer)) { break; } } } else { data.infile = stdin; } } data.contentLength = data.originalContentLength = contentLength; S3_init(); S3BucketContext bucketContext = { 0, bucketName, protocolG, uriStyleG, accessKeyIdG, secretAccessKeyG }; S3PutProperties putProperties = { contentType, md5, cacheControl, contentDispositionFilename, contentEncoding, expires, cannedAcl, metaPropertiesCount, metaProperties }; S3PutObjectHandler putObjectHandler = { { &responsePropertiesCallback, &responseCompleteCallback }, &putObjectDataCallback }; do { S3_put_object(&bucketContext, key, contentLength, &putProperties, 0, &putObjectHandler, &data); } while (S3_status_is_retryable(statusG) && should_retry()); if (data.infile) { fclose(data.infile); } else if (data.gb) { growbuffer_destroy(data.gb); } if (statusG != S3StatusOK) { printError(); } else if (data.contentLength) { fprintf(stderr, "\nERROR: Failed to read remaining %llu bytes from " "input\n", (unsigned long long) data.contentLength); } S3_deinitialize(); } // copy object --------------------------------------------------------------- static void copy_object(int argc, char **argv, int optindex) { if (optindex == argc) { fprintf(stderr, "\nERROR: Missing parameter: source bucket/key\n"); usageExit(stderr); } // Split bucket/key char *slash = argv[optindex]; while (*slash && (*slash != '/')) { slash++; } if (!*slash || !*(slash + 1)) { fprintf(stderr, "\nERROR: Invalid source bucket/key name: %s\n", argv[optindex]); usageExit(stderr); } *slash++ = 0; const char *sourceBucketName = argv[optindex++]; const char *sourceKey = slash; if (optindex == argc) { fprintf(stderr, "\nERROR: Missing parameter: " "destination bucket/key\n"); usageExit(stderr); } // Split bucket/key slash = argv[optindex]; while (*slash && (*slash != '/')) { slash++; } if (!*slash || !*(slash + 1)) { fprintf(stderr, "\nERROR: Invalid destination bucket/key name: %s\n", argv[optindex]); usageExit(stderr); } *slash++ = 0; const char *destinationBucketName = argv[optindex++]; const char *destinationKey = slash; const char *cacheControl = 0, *contentType = 0; const char *contentDispositionFilename = 0, *contentEncoding = 0; int64_t expires = -1; S3CannedAcl cannedAcl = S3CannedAclPrivate; int metaPropertiesCount = 0; S3NameValue metaProperties[S3_MAX_METADATA_COUNT]; int anyPropertiesSet = 0; while (optindex < argc) { char *param = argv[optindex++]; if (!strncmp(param, CACHE_CONTROL_PREFIX, CACHE_CONTROL_PREFIX_LEN)) { cacheControl = &(param[CACHE_CONTROL_PREFIX_LEN]); anyPropertiesSet = 1; } else if (!strncmp(param, CONTENT_TYPE_PREFIX, CONTENT_TYPE_PREFIX_LEN)) { contentType = &(param[CONTENT_TYPE_PREFIX_LEN]); anyPropertiesSet = 1; } else if (!strncmp(param, CONTENT_DISPOSITION_FILENAME_PREFIX, CONTENT_DISPOSITION_FILENAME_PREFIX_LEN)) { contentDispositionFilename = &(param[CONTENT_DISPOSITION_FILENAME_PREFIX_LEN]); anyPropertiesSet = 1; } else if (!strncmp(param, CONTENT_ENCODING_PREFIX, CONTENT_ENCODING_PREFIX_LEN)) { contentEncoding = &(param[CONTENT_ENCODING_PREFIX_LEN]); anyPropertiesSet = 1; } else if (!strncmp(param, EXPIRES_PREFIX, EXPIRES_PREFIX_LEN)) { expires = parseIso8601Time(&(param[EXPIRES_PREFIX_LEN])); if (expires < 0) { fprintf(stderr, "\nERROR: Invalid expires time " "value; ISO 8601 time format required\n"); usageExit(stderr); } anyPropertiesSet = 1; } else if (!strncmp(param, X_AMZ_META_PREFIX, X_AMZ_META_PREFIX_LEN)) { if (metaPropertiesCount == S3_MAX_METADATA_COUNT) { fprintf(stderr, "\nERROR: Too many x-amz-meta- properties, " "limit %lu: %s\n", (unsigned long) S3_MAX_METADATA_COUNT, param); usageExit(stderr); } char *name = &(param[X_AMZ_META_PREFIX_LEN]); char *value = name; while (*value && (*value != '=')) { value++; } if (!*value || !*(value + 1)) { fprintf(stderr, "\nERROR: Invalid parameter: %s\n", param); usageExit(stderr); } *value++ = 0; metaProperties[metaPropertiesCount].name = name; metaProperties[metaPropertiesCount++].value = value; anyPropertiesSet = 1; } else if (!strncmp(param, CANNED_ACL_PREFIX, CANNED_ACL_PREFIX_LEN)) { char *val = &(param[CANNED_ACL_PREFIX_LEN]); if (!strcmp(val, "private")) { cannedAcl = S3CannedAclPrivate; } else if (!strcmp(val, "public-read")) { cannedAcl = S3CannedAclPublicRead; } else if (!strcmp(val, "public-read-write")) { cannedAcl = S3CannedAclPublicReadWrite; } else if (!strcmp(val, "authenticated-read")) { cannedAcl = S3CannedAclAuthenticatedRead; } else { fprintf(stderr, "\nERROR: Unknown canned ACL: %s\n", val); usageExit(stderr); } anyPropertiesSet = 1; } else { fprintf(stderr, "\nERROR: Unknown param: %s\n", param); usageExit(stderr); } } S3_init(); S3BucketContext bucketContext = { 0, sourceBucketName, protocolG, uriStyleG, accessKeyIdG, secretAccessKeyG }; S3PutProperties putProperties = { contentType, 0, cacheControl, contentDispositionFilename, contentEncoding, expires, cannedAcl, metaPropertiesCount, metaProperties }; S3ResponseHandler responseHandler = { &responsePropertiesCallback, &responseCompleteCallback }; int64_t lastModified; char eTag[256]; do { S3_copy_object(&bucketContext, sourceKey, destinationBucketName, destinationKey, anyPropertiesSet ? &putProperties : 0, &lastModified, sizeof(eTag), eTag, 0, &responseHandler, 0); } while (S3_status_is_retryable(statusG) && should_retry()); if (statusG == S3StatusOK) { if (lastModified >= 0) { char timebuf[256]; time_t t = (time_t) lastModified; strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%SZ", gmtime(&t)); printf("Last-Modified: %s\n", timebuf); } if (eTag[0]) { printf("ETag: %s\n", eTag); } } else { printError(); } S3_deinitialize(); } // get object ---------------------------------------------------------------- static S3Status getObjectDataCallback(int bufferSize, const char *buffer, void *callbackData) { FILE *outfile = (FILE *) callbackData; size_t wrote = fwrite(buffer, 1, bufferSize, outfile); return ((wrote < (size_t) bufferSize) ? S3StatusAbortedByCallback : S3StatusOK); } static void get_object(int argc, char **argv, int optindex) { if (optindex == argc) { fprintf(stderr, "\nERROR: Missing parameter: bucket/key\n"); usageExit(stderr); } // Split bucket/key char *slash = argv[optindex]; while (*slash && (*slash != '/')) { slash++; } if (!*slash || !*(slash + 1)) { fprintf(stderr, "\nERROR: Invalid bucket/key name: %s\n", argv[optindex]); usageExit(stderr); } *slash++ = 0; const char *bucketName = argv[optindex++]; const char *key = slash; const char *filename = 0; int64_t ifModifiedSince = -1, ifNotModifiedSince = -1; const char *ifMatch = 0, *ifNotMatch = 0; uint64_t startByte = 0, byteCount = 0; while (optindex < argc) { char *param = argv[optindex++]; if (!strncmp(param, FILENAME_PREFIX, FILENAME_PREFIX_LEN)) { filename = &(param[FILENAME_PREFIX_LEN]); } else if (!strncmp(param, IF_MODIFIED_SINCE_PREFIX, IF_MODIFIED_SINCE_PREFIX_LEN)) { // Parse ifModifiedSince ifModifiedSince = parseIso8601Time (&(param[IF_MODIFIED_SINCE_PREFIX_LEN])); if (ifModifiedSince < 0) { fprintf(stderr, "\nERROR: Invalid ifModifiedSince time " "value; ISO 8601 time format required\n"); usageExit(stderr); } } else if (!strncmp(param, IF_NOT_MODIFIED_SINCE_PREFIX, IF_NOT_MODIFIED_SINCE_PREFIX_LEN)) { // Parse ifModifiedSince ifNotModifiedSince = parseIso8601Time (&(param[IF_NOT_MODIFIED_SINCE_PREFIX_LEN])); if (ifNotModifiedSince < 0) { fprintf(stderr, "\nERROR: Invalid ifNotModifiedSince time " "value; ISO 8601 time format required\n"); usageExit(stderr); } } else if (!strncmp(param, IF_MATCH_PREFIX, IF_MATCH_PREFIX_LEN)) { ifMatch = &(param[IF_MATCH_PREFIX_LEN]); } else if (!strncmp(param, IF_NOT_MATCH_PREFIX, IF_NOT_MATCH_PREFIX_LEN)) { ifNotMatch = &(param[IF_NOT_MATCH_PREFIX_LEN]); } else if (!strncmp(param, START_BYTE_PREFIX, START_BYTE_PREFIX_LEN)) { startByte = convertInt (&(param[START_BYTE_PREFIX_LEN]), "startByte"); } else if (!strncmp(param, BYTE_COUNT_PREFIX, BYTE_COUNT_PREFIX_LEN)) { byteCount = convertInt (&(param[BYTE_COUNT_PREFIX_LEN]), "byteCount"); } else { fprintf(stderr, "\nERROR: Unknown param: %s\n", param); usageExit(stderr); } } FILE *outfile = 0; if (filename) { // Stat the file, and if it doesn't exist, open it in w mode struct stat buf; if (stat(filename, &buf) == -1) { outfile = fopen(filename, "w" FOPEN_EXTRA_FLAGS); } else { // Open in r+ so that we don't truncate the file, just in case // there is an error and we write no bytes, we leave the file // unmodified outfile = fopen(filename, "r+" FOPEN_EXTRA_FLAGS); } if (!outfile) { fprintf(stderr, "\nERROR: Failed to open output file %s: ", filename); perror(0); exit(-1); } } else if (showResponsePropertiesG) { fprintf(stderr, "\nERROR: get -s requires a filename parameter\n"); usageExit(stderr); } else { outfile = stdout; } S3_init(); S3BucketContext bucketContext = { 0, bucketName, protocolG, uriStyleG, accessKeyIdG, secretAccessKeyG }; S3GetConditions getConditions = { ifModifiedSince, ifNotModifiedSince, ifMatch, ifNotMatch }; S3GetObjectHandler getObjectHandler = { { &responsePropertiesCallback, &responseCompleteCallback }, &getObjectDataCallback }; do { S3_get_object(&bucketContext, key, &getConditions, startByte, byteCount, 0, &getObjectHandler, outfile); } while (S3_status_is_retryable(statusG) && should_retry()); if (statusG != S3StatusOK) { printError(); } fclose(outfile); S3_deinitialize(); } // head object --------------------------------------------------------------- static void head_object(int argc, char **argv, int optindex) { if (optindex == argc) { fprintf(stderr, "\nERROR: Missing parameter: bucket/key\n"); usageExit(stderr); } // Head implies showing response properties showResponsePropertiesG = 1; // Split bucket/key char *slash = argv[optindex]; while (*slash && (*slash != '/')) { slash++; } if (!*slash || !*(slash + 1)) { fprintf(stderr, "\nERROR: Invalid bucket/key name: %s\n", argv[optindex]); usageExit(stderr); } *slash++ = 0; const char *bucketName = argv[optindex++]; const char *key = slash; if (optindex != argc) { fprintf(stderr, "\nERROR: Extraneous parameter: %s\n", argv[optindex]); usageExit(stderr); } S3_init(); S3BucketContext bucketContext = { 0, bucketName, protocolG, uriStyleG, accessKeyIdG, secretAccessKeyG }; S3ResponseHandler responseHandler = { &responsePropertiesCallback, &responseCompleteCallback }; do { S3_head_object(&bucketContext, key, 0, &responseHandler, 0); } while (S3_status_is_retryable(statusG) && should_retry()); if ((statusG != S3StatusOK) && (statusG != S3StatusErrorPreconditionFailed)) { printError(); } S3_deinitialize(); } // generate query string ------------------------------------------------------ static void generate_query_string(int argc, char **argv, int optindex) { if (optindex == argc) { fprintf(stderr, "\nERROR: Missing parameter: bucket[/key]\n"); usageExit(stderr); } const char *bucketName = argv[optindex]; const char *key = 0; // Split bucket/key char *slash = argv[optindex++]; while (*slash && (*slash != '/')) { slash++; } if (*slash) { *slash++ = 0; key = slash; } else { key = 0; } int64_t expires = -1; const char *resource = 0; while (optindex < argc) { char *param = argv[optindex++]; if (!strncmp(param, EXPIRES_PREFIX, EXPIRES_PREFIX_LEN)) { expires = parseIso8601Time(&(param[EXPIRES_PREFIX_LEN])); if (expires < 0) { fprintf(stderr, "\nERROR: Invalid expires time " "value; ISO 8601 time format required\n"); usageExit(stderr); } } else if (!strncmp(param, RESOURCE_PREFIX, RESOURCE_PREFIX_LEN)) { resource = &(param[RESOURCE_PREFIX_LEN]); } else { fprintf(stderr, "\nERROR: Unknown param: %s\n", param); usageExit(stderr); } } S3_init(); S3BucketContext bucketContext = { 0, bucketName, protocolG, uriStyleG, accessKeyIdG, secretAccessKeyG }; char buffer[S3_MAX_AUTHENTICATED_QUERY_STRING_SIZE]; S3Status status = S3_generate_authenticated_query_string (buffer, &bucketContext, key, expires, resource); if (status != S3StatusOK) { printf("Failed to generate authenticated query string: %s\n", S3_get_status_name(status)); } else { printf("%s\n", buffer); } S3_deinitialize(); } // get acl ------------------------------------------------------------------- void get_acl(int argc, char **argv, int optindex) { if (optindex == argc) { fprintf(stderr, "\nERROR: Missing parameter: bucket[/key]\n"); usageExit(stderr); } const char *bucketName = argv[optindex]; const char *key = 0; // Split bucket/key char *slash = argv[optindex++]; while (*slash && (*slash != '/')) { slash++; } if (*slash) { *slash++ = 0; key = slash; } else { key = 0; } const char *filename = 0; while (optindex < argc) { char *param = argv[optindex++]; if (!strncmp(param, FILENAME_PREFIX, FILENAME_PREFIX_LEN)) { filename = &(param[FILENAME_PREFIX_LEN]); } else { fprintf(stderr, "\nERROR: Unknown param: %s\n", param); usageExit(stderr); } } FILE *outfile = 0; if (filename) { // Stat the file, and if it doesn't exist, open it in w mode struct stat buf; if (stat(filename, &buf) == -1) { outfile = fopen(filename, "w" FOPEN_EXTRA_FLAGS); } else { // Open in r+ so that we don't truncate the file, just in case // there is an error and we write no bytes, we leave the file // unmodified outfile = fopen(filename, "r+" FOPEN_EXTRA_FLAGS); } if (!outfile) { fprintf(stderr, "\nERROR: Failed to open output file %s: ", filename); perror(0); exit(-1); } } else if (showResponsePropertiesG) { fprintf(stderr, "\nERROR: getacl -s requires a filename parameter\n"); usageExit(stderr); } else { outfile = stdout; } int aclGrantCount; S3AclGrant aclGrants[S3_MAX_ACL_GRANT_COUNT]; char ownerId[S3_MAX_GRANTEE_USER_ID_SIZE]; char ownerDisplayName[S3_MAX_GRANTEE_DISPLAY_NAME_SIZE]; S3_init(); S3BucketContext bucketContext = { 0, bucketName, protocolG, uriStyleG, accessKeyIdG, secretAccessKeyG }; S3ResponseHandler responseHandler = { &responsePropertiesCallback, &responseCompleteCallback }; do { S3_get_acl(&bucketContext, key, ownerId, ownerDisplayName, &aclGrantCount, aclGrants, 0, &responseHandler, 0); } while (S3_status_is_retryable(statusG) && should_retry()); if (statusG == S3StatusOK) { fprintf(outfile, "OwnerID %s %s\n", ownerId, ownerDisplayName); fprintf(outfile, "%-6s %-90s %-12s\n", " Type", " User Identifier", " Permission"); fprintf(outfile, "------ " "------------------------------------------------------------" "------------------------------ ------------\n"); int i; for (i = 0; i < aclGrantCount; i++) { S3AclGrant *grant = &(aclGrants[i]); const char *type; char composedId[S3_MAX_GRANTEE_USER_ID_SIZE + S3_MAX_GRANTEE_DISPLAY_NAME_SIZE + 16]; const char *id; switch (grant->granteeType) { case S3GranteeTypeAmazonCustomerByEmail: type = "Email"; id = grant->grantee.amazonCustomerByEmail.emailAddress; break; case S3GranteeTypeCanonicalUser: type = "UserID"; snprintf(composedId, sizeof(composedId), "%s (%s)", grant->grantee.canonicalUser.id, grant->grantee.canonicalUser.displayName); id = composedId; break; case S3GranteeTypeAllAwsUsers: type = "Group"; id = "Authenticated AWS Users"; break; case S3GranteeTypeAllUsers: type = "Group"; id = "All Users"; break; default: type = "Group"; id = "Log Delivery"; break; } const char *perm; switch (grant->permission) { case S3PermissionRead: perm = "READ"; break; case S3PermissionWrite: perm = "WRITE"; break; case S3PermissionReadACP: perm = "READ_ACP"; break; case S3PermissionWriteACP: perm = "WRITE_ACP"; break; default: perm = "FULL_CONTROL"; break; } fprintf(outfile, "%-6s %-90s %-12s\n", type, id, perm); } } else { printError(); } fclose(outfile); S3_deinitialize(); } // set acl ------------------------------------------------------------------- void set_acl(int argc, char **argv, int optindex) { if (optindex == argc) { fprintf(stderr, "\nERROR: Missing parameter: bucket[/key]\n"); usageExit(stderr); } const char *bucketName = argv[optindex]; const char *key = 0; // Split bucket/key char *slash = argv[optindex++]; while (*slash && (*slash != '/')) { slash++; } if (*slash) { *slash++ = 0; key = slash; } else { key = 0; } const char *filename = 0; while (optindex < argc) { char *param = argv[optindex++]; if (!strncmp(param, FILENAME_PREFIX, FILENAME_PREFIX_LEN)) { filename = &(param[FILENAME_PREFIX_LEN]); } else { fprintf(stderr, "\nERROR: Unknown param: %s\n", param); usageExit(stderr); } } FILE *infile; if (filename) { if (!(infile = fopen(filename, "r" FOPEN_EXTRA_FLAGS))) { fprintf(stderr, "\nERROR: Failed to open input file %s: ", filename); perror(0); exit(-1); } } else { infile = stdin; } // Read in the complete ACL char aclBuf[65536]; aclBuf[fread(aclBuf, 1, sizeof(aclBuf), infile)] = 0; char ownerId[S3_MAX_GRANTEE_USER_ID_SIZE]; char ownerDisplayName[S3_MAX_GRANTEE_DISPLAY_NAME_SIZE]; // Parse it int aclGrantCount; S3AclGrant aclGrants[S3_MAX_ACL_GRANT_COUNT]; if (!convert_simple_acl(aclBuf, ownerId, ownerDisplayName, &aclGrantCount, aclGrants)) { fprintf(stderr, "\nERROR: Failed to parse ACLs\n"); fclose(infile); exit(-1); } S3_init(); S3BucketContext bucketContext = { 0, bucketName, protocolG, uriStyleG, accessKeyIdG, secretAccessKeyG }; S3ResponseHandler responseHandler = { &responsePropertiesCallback, &responseCompleteCallback }; do { S3_set_acl(&bucketContext, key, ownerId, ownerDisplayName, aclGrantCount, aclGrants, 0, &responseHandler, 0); } while (S3_status_is_retryable(statusG) && should_retry()); if (statusG != S3StatusOK) { printError(); } fclose(infile); S3_deinitialize(); } // get logging ---------------------------------------------------------------- void get_logging(int argc, char **argv, int optindex) { if (optindex == argc) { fprintf(stderr, "\nERROR: Missing parameter: bucket\n"); usageExit(stderr); } const char *bucketName = argv[optindex++]; const char *filename = 0; while (optindex < argc) { char *param = argv[optindex++]; if (!strncmp(param, FILENAME_PREFIX, FILENAME_PREFIX_LEN)) { filename = &(param[FILENAME_PREFIX_LEN]); } else { fprintf(stderr, "\nERROR: Unknown param: %s\n", param); usageExit(stderr); } } FILE *outfile = 0; if (filename) { // Stat the file, and if it doesn't exist, open it in w mode struct stat buf; if (stat(filename, &buf) == -1) { outfile = fopen(filename, "w" FOPEN_EXTRA_FLAGS); } else { // Open in r+ so that we don't truncate the file, just in case // there is an error and we write no bytes, we leave the file // unmodified outfile = fopen(filename, "r+" FOPEN_EXTRA_FLAGS); } if (!outfile) { fprintf(stderr, "\nERROR: Failed to open output file %s: ", filename); perror(0); exit(-1); } } else if (showResponsePropertiesG) { fprintf(stderr, "\nERROR: getlogging -s requires a filename " "parameter\n"); usageExit(stderr); } else { outfile = stdout; } int aclGrantCount; S3AclGrant aclGrants[S3_MAX_ACL_GRANT_COUNT]; char targetBucket[S3_MAX_BUCKET_NAME_SIZE]; char targetPrefix[S3_MAX_KEY_SIZE]; S3_init(); S3BucketContext bucketContext = { 0, bucketName, protocolG, uriStyleG, accessKeyIdG, secretAccessKeyG }; S3ResponseHandler responseHandler = { &responsePropertiesCallback, &responseCompleteCallback }; do { S3_get_server_access_logging(&bucketContext, targetBucket, targetPrefix, &aclGrantCount, aclGrants, 0, &responseHandler, 0); } while (S3_status_is_retryable(statusG) && should_retry()); if (statusG == S3StatusOK) { if (targetBucket[0]) { printf("Target Bucket: %s\n", targetBucket); if (targetPrefix[0]) { printf("Target Prefix: %s\n", targetPrefix); } fprintf(outfile, "%-6s %-90s %-12s\n", " Type", " User Identifier", " Permission"); fprintf(outfile, "------ " "---------------------------------------------------------" "--------------------------------- ------------\n"); int i; for (i = 0; i < aclGrantCount; i++) { S3AclGrant *grant = &(aclGrants[i]); const char *type; char composedId[S3_MAX_GRANTEE_USER_ID_SIZE + S3_MAX_GRANTEE_DISPLAY_NAME_SIZE + 16]; const char *id; switch (grant->granteeType) { case S3GranteeTypeAmazonCustomerByEmail: type = "Email"; id = grant->grantee.amazonCustomerByEmail.emailAddress; break; case S3GranteeTypeCanonicalUser: type = "UserID"; snprintf(composedId, sizeof(composedId), "%s (%s)", grant->grantee.canonicalUser.id, grant->grantee.canonicalUser.displayName); id = composedId; break; case S3GranteeTypeAllAwsUsers: type = "Group"; id = "Authenticated AWS Users"; break; default: type = "Group"; id = "All Users"; break; } const char *perm; switch (grant->permission) { case S3PermissionRead: perm = "READ"; break; case S3PermissionWrite: perm = "WRITE"; break; case S3PermissionReadACP: perm = "READ_ACP"; break; case S3PermissionWriteACP: perm = "WRITE_ACP"; break; default: perm = "FULL_CONTROL"; break; } fprintf(outfile, "%-6s %-90s %-12s\n", type, id, perm); } } else { printf("Service logging is not enabled for this bucket.\n"); } } else { printError(); } fclose(outfile); S3_deinitialize(); } // set logging ---------------------------------------------------------------- void set_logging(int argc, char **argv, int optindex) { if (optindex == argc) { fprintf(stderr, "\nERROR: Missing parameter: bucket\n"); usageExit(stderr); } const char *bucketName = argv[optindex++]; const char *targetBucket = 0, *targetPrefix = 0, *filename = 0; while (optindex < argc) { char *param = argv[optindex++]; if (!strncmp(param, TARGET_BUCKET_PREFIX, TARGET_BUCKET_PREFIX_LEN)) { targetBucket = &(param[TARGET_BUCKET_PREFIX_LEN]); } else if (!strncmp(param, TARGET_PREFIX_PREFIX, TARGET_PREFIX_PREFIX_LEN)) { targetPrefix = &(param[TARGET_PREFIX_PREFIX_LEN]); } else if (!strncmp(param, FILENAME_PREFIX, FILENAME_PREFIX_LEN)) { filename = &(param[FILENAME_PREFIX_LEN]); } else { fprintf(stderr, "\nERROR: Unknown param: %s\n", param); usageExit(stderr); } } int aclGrantCount = 0; S3AclGrant aclGrants[S3_MAX_ACL_GRANT_COUNT]; if (targetBucket) { FILE *infile; if (filename) { if (!(infile = fopen(filename, "r" FOPEN_EXTRA_FLAGS))) { fprintf(stderr, "\nERROR: Failed to open input file %s: ", filename); perror(0); exit(-1); } } else { infile = stdin; } // Read in the complete ACL char aclBuf[65536]; aclBuf[fread(aclBuf, 1, sizeof(aclBuf), infile)] = 0; char ownerId[S3_MAX_GRANTEE_USER_ID_SIZE]; char ownerDisplayName[S3_MAX_GRANTEE_DISPLAY_NAME_SIZE]; // Parse it if (!convert_simple_acl(aclBuf, ownerId, ownerDisplayName, &aclGrantCount, aclGrants)) { fprintf(stderr, "\nERROR: Failed to parse ACLs\n"); fclose(infile); exit(-1); } fclose(infile); } S3_init(); S3BucketContext bucketContext = { 0, bucketName, protocolG, uriStyleG, accessKeyIdG, secretAccessKeyG }; S3ResponseHandler responseHandler = { &responsePropertiesCallback, &responseCompleteCallback }; do { S3_set_server_access_logging(&bucketContext, targetBucket, targetPrefix, aclGrantCount, aclGrants, 0, &responseHandler, 0); } while (S3_status_is_retryable(statusG) && should_retry()); if (statusG != S3StatusOK) { printError(); } S3_deinitialize(); } // main ---------------------------------------------------------------------- int main(int argc, char **argv) { // Parse args while (1) { int idx = 0; int c = getopt_long(argc, argv, "fhusr:", longOptionsG, &idx); if (c == -1) { // End of options break; } switch (c) { case 'f': forceG = 1; break; case 'h': uriStyleG = S3UriStyleVirtualHost; break; case 'u': protocolG = S3ProtocolHTTP; break; case 's': showResponsePropertiesG = 1; break; case 'r': { const char *v = optarg; retriesG = 0; while (*v) { retriesG *= 10; retriesG += *v - '0'; v++; } break; } default: fprintf(stderr, "\nERROR: Unknown option: -%c\n", c); // Usage exit usageExit(stderr); } } // The first non-option argument gives the operation to perform if (optind == argc) { fprintf(stderr, "\n\nERROR: Missing argument: command\n\n"); usageExit(stderr); } const char *command = argv[optind++]; if (!strcmp(command, "help")) { fprintf(stdout, "\ns3 is a program for performing single requests " "to Amazon S3.\n"); usageExit(stdout); } accessKeyIdG = getenv("S3_ACCESS_KEY_ID"); if (!accessKeyIdG) { fprintf(stderr, "Missing environment variable: S3_ACCESS_KEY_ID\n"); return -1; } secretAccessKeyG = getenv("S3_SECRET_ACCESS_KEY"); if (!secretAccessKeyG) { fprintf(stderr, "Missing environment variable: S3_SECRET_ACCESS_KEY\n"); return -1; } if (!strcmp(command, "list")) { list(argc, argv, optind); } else if (!strcmp(command, "test")) { test_bucket(argc, argv, optind); } else if (!strcmp(command, "create")) { create_bucket(argc, argv, optind); } else if (!strcmp(command, "delete")) { if (optind == argc) { fprintf(stderr, "\nERROR: Missing parameter: bucket or bucket/key\n"); usageExit(stderr); } char *val = argv[optind]; int hasSlash = 0; while (*val) { if (*val++ == '/') { hasSlash = 1; break; } } if (hasSlash) { delete_object(argc, argv, optind); } else { delete_bucket(argc, argv, optind); } } else if (!strcmp(command, "put")) { put_object(argc, argv, optind); } else if (!strcmp(command, "copy")) { copy_object(argc, argv, optind); } else if (!strcmp(command, "get")) { get_object(argc, argv, optind); } else if (!strcmp(command, "head")) { head_object(argc, argv, optind); } else if (!strcmp(command, "gqs")) { generate_query_string(argc, argv, optind); } else if (!strcmp(command, "getacl")) { get_acl(argc, argv, optind); } else if (!strcmp(command, "setacl")) { set_acl(argc, argv, optind); } else if (!strcmp(command, "getlogging")) { get_logging(argc, argv, optind); } else if (!strcmp(command, "setlogging")) { set_logging(argc, argv, optind); } else { fprintf(stderr, "Unknown command: %s\n", command); return -1; } return 0; } libs3-2.0/src/error_parser.c0000644000175000001440000002126511640553711014523 0ustar bjiusers/** ************************************************************************** * error_parser.c * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ #include #include "error_parser.h" static S3Status errorXmlCallback(const char *elementPath, const char *data, int dataLen, void *callbackData) { // We ignore end of element callbacks because we don't care about them if (!data) { return S3StatusOK; } ErrorParser *errorParser = (ErrorParser *) callbackData; int fit; if (!strcmp(elementPath, "Error")) { // Ignore, this is the Error element itself, we only care about subs } else if (!strcmp(elementPath, "Error/Code")) { string_buffer_append(errorParser->code, data, dataLen, fit); } else if (!strcmp(elementPath, "Error/Message")) { string_buffer_append(errorParser->message, data, dataLen, fit); errorParser->s3ErrorDetails.message = errorParser->message; } else if (!strcmp(elementPath, "Error/Resource")) { string_buffer_append(errorParser->resource, data, dataLen, fit); errorParser->s3ErrorDetails.resource = errorParser->resource; } else if (!strcmp(elementPath, "Error/FurtherDetails")) { string_buffer_append(errorParser->furtherDetails, data, dataLen, fit); errorParser->s3ErrorDetails.furtherDetails = errorParser->furtherDetails; } else { if (strncmp(elementPath, "Error/", sizeof("Error/") - 1)) { // If for some weird reason it's not within the Error element, // ignore it return S3StatusOK; } // It's an unknown error element. See if it matches the most // recent error element. const char *elementName = &(elementPath[sizeof("Error/") - 1]); if (errorParser->s3ErrorDetails.extraDetailsCount && !strcmp(elementName, errorParser->s3ErrorDetails.extraDetails [errorParser->s3ErrorDetails.extraDetailsCount - 1].name)) { // Append the value string_multibuffer_append(errorParser->extraDetailsNamesValues, data, dataLen, fit); // If it didn't fit, remove this extra if (!fit) { errorParser->s3ErrorDetails.extraDetailsCount--; } return S3StatusOK; } // OK, must add another unknown error element, if it will fit. if (errorParser->s3ErrorDetails.extraDetailsCount == sizeof(errorParser->extraDetails)) { // Won't fit. Ignore this one. return S3StatusOK; } // Copy in the name and value char *name = string_multibuffer_current (errorParser->extraDetailsNamesValues); int nameLen = strlen(elementName); string_multibuffer_add(errorParser->extraDetailsNamesValues, elementName, nameLen, fit); if (!fit) { // Name didn't fit; ignore this one. return S3StatusOK; } char *value = string_multibuffer_current (errorParser->extraDetailsNamesValues); string_multibuffer_add(errorParser->extraDetailsNamesValues, data, dataLen, fit); if (!fit) { // Value didn't fit; ignore this one. return S3StatusOK; } S3NameValue *nv = &(errorParser->extraDetails [errorParser->s3ErrorDetails.extraDetailsCount++]); nv->name = name; nv->value = value; } return S3StatusOK; } void error_parser_initialize(ErrorParser *errorParser) { errorParser->s3ErrorDetails.message = 0; errorParser->s3ErrorDetails.resource = 0; errorParser->s3ErrorDetails.furtherDetails = 0; errorParser->s3ErrorDetails.extraDetailsCount = 0; errorParser->s3ErrorDetails.extraDetails = errorParser->extraDetails; errorParser->errorXmlParserInitialized = 0; string_buffer_initialize(errorParser->code); string_buffer_initialize(errorParser->message); string_buffer_initialize(errorParser->resource); string_buffer_initialize(errorParser->furtherDetails); string_multibuffer_initialize(errorParser->extraDetailsNamesValues); } S3Status error_parser_add(ErrorParser *errorParser, char *buffer, int bufferSize) { if (!errorParser->errorXmlParserInitialized) { simplexml_initialize(&(errorParser->errorXmlParser), &errorXmlCallback, errorParser); errorParser->errorXmlParserInitialized = 1; } return simplexml_add(&(errorParser->errorXmlParser), buffer, bufferSize); } void error_parser_convert_status(ErrorParser *errorParser, S3Status *status) { // Convert the error status string into a code if (!errorParser->codeLen) { return; } #define HANDLE_CODE(name) \ do { \ if (!strcmp(errorParser->code, #name)) { \ *status = S3StatusError##name; \ goto code_set; \ } \ } while (0) HANDLE_CODE(AccessDenied); HANDLE_CODE(AccountProblem); HANDLE_CODE(AmbiguousGrantByEmailAddress); HANDLE_CODE(BadDigest); HANDLE_CODE(BucketAlreadyExists); HANDLE_CODE(BucketAlreadyOwnedByYou); HANDLE_CODE(BucketNotEmpty); HANDLE_CODE(CredentialsNotSupported); HANDLE_CODE(CrossLocationLoggingProhibited); HANDLE_CODE(EntityTooSmall); HANDLE_CODE(EntityTooLarge); HANDLE_CODE(ExpiredToken); HANDLE_CODE(IncompleteBody); HANDLE_CODE(IncorrectNumberOfFilesInPostRequest); HANDLE_CODE(InlineDataTooLarge); HANDLE_CODE(InternalError); HANDLE_CODE(InvalidAccessKeyId); HANDLE_CODE(InvalidAddressingHeader); HANDLE_CODE(InvalidArgument); HANDLE_CODE(InvalidBucketName); HANDLE_CODE(InvalidDigest); HANDLE_CODE(InvalidLocationConstraint); HANDLE_CODE(InvalidPayer); HANDLE_CODE(InvalidPolicyDocument); HANDLE_CODE(InvalidRange); HANDLE_CODE(InvalidSecurity); HANDLE_CODE(InvalidSOAPRequest); HANDLE_CODE(InvalidStorageClass); HANDLE_CODE(InvalidTargetBucketForLogging); HANDLE_CODE(InvalidToken); HANDLE_CODE(InvalidURI); HANDLE_CODE(KeyTooLong); HANDLE_CODE(MalformedACLError); HANDLE_CODE(MalformedXML); HANDLE_CODE(MaxMessageLengthExceeded); HANDLE_CODE(MaxPostPreDataLengthExceededError); HANDLE_CODE(MetadataTooLarge); HANDLE_CODE(MethodNotAllowed); HANDLE_CODE(MissingAttachment); HANDLE_CODE(MissingContentLength); HANDLE_CODE(MissingSecurityElement); HANDLE_CODE(MissingSecurityHeader); HANDLE_CODE(NoLoggingStatusForKey); HANDLE_CODE(NoSuchBucket); HANDLE_CODE(NoSuchKey); HANDLE_CODE(NotImplemented); HANDLE_CODE(NotSignedUp); HANDLE_CODE(OperationAborted); HANDLE_CODE(PermanentRedirect); HANDLE_CODE(PreconditionFailed); HANDLE_CODE(Redirect); HANDLE_CODE(RequestIsNotMultiPartContent); HANDLE_CODE(RequestTimeout); HANDLE_CODE(RequestTimeTooSkewed); HANDLE_CODE(RequestTorrentOfBucketError); HANDLE_CODE(SignatureDoesNotMatch); HANDLE_CODE(SlowDown); HANDLE_CODE(TemporaryRedirect); HANDLE_CODE(TokenRefreshRequired); HANDLE_CODE(TooManyBuckets); HANDLE_CODE(UnexpectedContent); HANDLE_CODE(UnresolvableGrantByEmailAddress); HANDLE_CODE(UserKeyMustBeSpecified); *status = S3StatusErrorUnknown; code_set: return; } // Always call this void error_parser_deinitialize(ErrorParser *errorParser) { if (errorParser->errorXmlParserInitialized) { simplexml_deinitialize(&(errorParser->errorXmlParser)); } } libs3-2.0/src/response_headers_handler.c0000644000175000001440000001612111640553711017037 0ustar bjiusers/** ************************************************************************** * response_headers_handler.c * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ #include #include #include "response_headers_handler.h" void response_headers_handler_initialize(ResponseHeadersHandler *handler) { handler->responseProperties.requestId = 0; handler->responseProperties.requestId2 = 0; handler->responseProperties.contentType = 0; handler->responseProperties.contentLength = 0; handler->responseProperties.server = 0; handler->responseProperties.eTag = 0; handler->responseProperties.lastModified = -1; handler->responseProperties.metaDataCount = 0; handler->responseProperties.metaData = 0; handler->done = 0; string_multibuffer_initialize(handler->responsePropertyStrings); string_multibuffer_initialize(handler->responseMetaDataStrings); } void response_headers_handler_add(ResponseHeadersHandler *handler, char *header, int len) { S3ResponseProperties *responseProperties = &(handler->responseProperties); char *end = &(header[len]); // Curl might call back the header function after the body has been // received, for 'chunked encoded' contents. We don't handle this as of // yet, and it's not clear that it would ever be useful. if (handler->done) { return; } // If we've already filled up the response headers, ignore this data. // This sucks, but it shouldn't happen - S3 should not be sending back // really long headers. if (handler->responsePropertyStringsSize == (sizeof(handler->responsePropertyStrings) - 1)) { return; } // It should not be possible to have a header line less than 3 long if (len < 3) { return; } // Skip whitespace at beginning of header; there never should be any, // but just to be safe while (is_blank(*header)) { header++; } // The header must end in \r\n, so skip back over it, and also over any // trailing whitespace end -= 3; while ((end > header) && is_blank(*end)) { end--; } if (!is_blank(*end)) { end++; } if (end == header) { // totally bogus return; } *end = 0; // Find the colon to split the header up char *c = header; while (*c && (*c != ':')) { c++; } int namelen = c - header; // Now walk c past the colon c++; // Now skip whitespace to the beginning of the value while (is_blank(*c)) { c++; } int valuelen = (end - c) + 1, fit; if (!strncmp(header, "x-amz-request-id", namelen)) { responseProperties->requestId = string_multibuffer_current(handler->responsePropertyStrings); string_multibuffer_add(handler->responsePropertyStrings, c, valuelen, fit); } else if (!strncmp(header, "x-amz-id-2", namelen)) { responseProperties->requestId2 = string_multibuffer_current(handler->responsePropertyStrings); string_multibuffer_add(handler->responsePropertyStrings, c, valuelen, fit); } else if (!strncmp(header, "Content-Type", namelen)) { responseProperties->contentType = string_multibuffer_current(handler->responsePropertyStrings); string_multibuffer_add(handler->responsePropertyStrings, c, valuelen, fit); } else if (!strncmp(header, "Content-Length", namelen)) { handler->responseProperties.contentLength = 0; while (*c) { handler->responseProperties.contentLength *= 10; handler->responseProperties.contentLength += (*c++ - '0'); } } else if (!strncmp(header, "Server", namelen)) { responseProperties->server = string_multibuffer_current(handler->responsePropertyStrings); string_multibuffer_add(handler->responsePropertyStrings, c, valuelen, fit); } else if (!strncmp(header, "ETag", namelen)) { responseProperties->eTag = string_multibuffer_current(handler->responsePropertyStrings); string_multibuffer_add(handler->responsePropertyStrings, c, valuelen, fit); } else if (!strncmp(header, S3_METADATA_HEADER_NAME_PREFIX, sizeof(S3_METADATA_HEADER_NAME_PREFIX) - 1)) { // Make sure there is room for another x-amz-meta header if (handler->responseProperties.metaDataCount == sizeof(handler->responseMetaData)) { return; } // Copy the name in char *metaName = &(header[sizeof(S3_METADATA_HEADER_NAME_PREFIX) - 1]); int metaNameLen = (namelen - (sizeof(S3_METADATA_HEADER_NAME_PREFIX) - 1)); char *copiedName = string_multibuffer_current(handler->responseMetaDataStrings); string_multibuffer_add(handler->responseMetaDataStrings, metaName, metaNameLen, fit); if (!fit) { return; } // Copy the value in char *copiedValue = string_multibuffer_current(handler->responseMetaDataStrings); string_multibuffer_add(handler->responseMetaDataStrings, c, valuelen, fit); if (!fit) { return; } if (!handler->responseProperties.metaDataCount) { handler->responseProperties.metaData = handler->responseMetaData; } S3NameValue *metaHeader = &(handler->responseMetaData [handler->responseProperties.metaDataCount++]); metaHeader->name = copiedName; metaHeader->value = copiedValue; } } void response_headers_handler_done(ResponseHeadersHandler *handler, CURL *curl) { // Now get the last modification time from curl, since it's easiest to let // curl parse it time_t lastModified; if (curl_easy_getinfo (curl, CURLINFO_FILETIME, &lastModified) == CURLE_OK) { handler->responseProperties.lastModified = lastModified; } handler->done = 1; } libs3-2.0/src/request.c0000644000175000001440000014162511640553711013511 0ustar bjiusers/** ************************************************************************** * request.c * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ #include #include #include #include #include #include "request.h" #include "request_context.h" #include "response_headers_handler.h" #include "util.h" #define USER_AGENT_SIZE 256 #define REQUEST_STACK_SIZE 32 static char userAgentG[USER_AGENT_SIZE]; static pthread_mutex_t requestStackMutexG; static Request *requestStackG[REQUEST_STACK_SIZE]; static int requestStackCountG; char defaultHostNameG[S3_MAX_HOSTNAME_SIZE]; typedef struct RequestComputedValues { // All x-amz- headers, in normalized form (i.e. NAME: VALUE, no other ws) char *amzHeaders[S3_MAX_METADATA_COUNT + 2]; // + 2 for acl and date // The number of x-amz- headers int amzHeadersCount; // Storage for amzHeaders (the +256 is for x-amz-acl and x-amz-date) char amzHeadersRaw[COMPACTED_METADATA_BUFFER_SIZE + 256 + 1]; // Canonicalized x-amz- headers string_multibuffer(canonicalizedAmzHeaders, COMPACTED_METADATA_BUFFER_SIZE + 256 + 1); // URL-Encoded key char urlEncodedKey[MAX_URLENCODED_KEY_SIZE + 1]; // Canonicalized resource char canonicalizedResource[MAX_CANONICALIZED_RESOURCE_SIZE + 1]; // Cache-Control header (or empty) char cacheControlHeader[128]; // Content-Type header (or empty) char contentTypeHeader[128]; // Content-MD5 header (or empty) char md5Header[128]; // Content-Disposition header (or empty) char contentDispositionHeader[128]; // Content-Encoding header (or empty) char contentEncodingHeader[128]; // Expires header (or empty) char expiresHeader[128]; // If-Modified-Since header char ifModifiedSinceHeader[128]; // If-Unmodified-Since header char ifUnmodifiedSinceHeader[128]; // If-Match header char ifMatchHeader[128]; // If-None-Match header char ifNoneMatchHeader[128]; // Range header char rangeHeader[128]; // Authorization header char authorizationHeader[128]; } RequestComputedValues; // Called whenever we detect that the request headers have been completely // processed; which happens either when we get our first read/write callback, // or the request is finished being procesed. Returns nonzero on success, // zero on failure. static void request_headers_done(Request *request) { if (request->propertiesCallbackMade) { return; } request->propertiesCallbackMade = 1; // Get the http response code long httpResponseCode; request->httpResponseCode = 0; if (curl_easy_getinfo(request->curl, CURLINFO_RESPONSE_CODE, &httpResponseCode) != CURLE_OK) { // Not able to get the HTTP response code - error request->status = S3StatusInternalError; return; } else { request->httpResponseCode = httpResponseCode; } response_headers_handler_done(&(request->responseHeadersHandler), request->curl); // Only make the callback if it was a successful request; otherwise we're // returning information about the error response itself if (request->propertiesCallback && (request->httpResponseCode >= 200) && (request->httpResponseCode <= 299)) { request->status = (*(request->propertiesCallback)) (&(request->responseHeadersHandler.responseProperties), request->callbackData); } } static size_t curl_header_func(void *ptr, size_t size, size_t nmemb, void *data) { Request *request = (Request *) data; int len = size * nmemb; response_headers_handler_add (&(request->responseHeadersHandler), (char *) ptr, len); return len; } static size_t curl_read_func(void *ptr, size_t size, size_t nmemb, void *data) { Request *request = (Request *) data; int len = size * nmemb; request_headers_done(request); if (request->status != S3StatusOK) { return CURL_READFUNC_ABORT; } // If there is no data callback, or the data callback has already returned // contentLength bytes, return 0; if (!request->toS3Callback || !request->toS3CallbackBytesRemaining) { return 0; } // Don't tell the callback that we are willing to accept more data than we // really are if (len > request->toS3CallbackBytesRemaining) { len = request->toS3CallbackBytesRemaining; } // Otherwise, make the data callback int ret = (*(request->toS3Callback)) (len, (char *) ptr, request->callbackData); if (ret < 0) { request->status = S3StatusAbortedByCallback; return CURL_READFUNC_ABORT; } else { if (ret > request->toS3CallbackBytesRemaining) { ret = request->toS3CallbackBytesRemaining; } request->toS3CallbackBytesRemaining -= ret; return ret; } } static size_t curl_write_func(void *ptr, size_t size, size_t nmemb, void *data) { Request *request = (Request *) data; int len = size * nmemb; request_headers_done(request); if (request->status != S3StatusOK) { return 0; } // On HTTP error, we expect to parse an HTTP error response if ((request->httpResponseCode < 200) || (request->httpResponseCode > 299)) { request->status = error_parser_add (&(request->errorParser), (char *) ptr, len); } // If there was a callback registered, make it else if (request->fromS3Callback) { request->status = (*(request->fromS3Callback)) (len, (char *) ptr, request->callbackData); } // Else, consider this an error - S3 has sent back data when it was not // expected else { request->status = S3StatusInternalError; } return ((request->status == S3StatusOK) ? len : 0); } // This function 'normalizes' all x-amz-meta headers provided in // params->requestHeaders, which means it removes all whitespace from // them such that they all look exactly like this: // x-amz-meta-${NAME}: ${VALUE} // It also adds the x-amz-acl, x-amz-copy-source, and x-amz-metadata-directive // headers if necessary, and always adds the x-amz-date header. It copies the // raw string values into params->amzHeadersRaw, and creates an array of // string pointers representing these headers in params->amzHeaders (and also // sets params->amzHeadersCount to be the count of the total number of x-amz- // headers thus created). static S3Status compose_amz_headers(const RequestParams *params, RequestComputedValues *values) { const S3PutProperties *properties = params->putProperties; values->amzHeadersCount = 0; values->amzHeadersRaw[0] = 0; int len = 0; // Append a header to amzHeaders, trimming whitespace from the end. // Does NOT trim whitespace from the beginning. #define headers_append(isNewHeader, format, ...) \ do { \ if (isNewHeader) { \ values->amzHeaders[values->amzHeadersCount++] = \ &(values->amzHeadersRaw[len]); \ } \ len += snprintf(&(values->amzHeadersRaw[len]), \ sizeof(values->amzHeadersRaw) - len, \ format, __VA_ARGS__); \ if (len >= (int) sizeof(values->amzHeadersRaw)) { \ return S3StatusMetaDataHeadersTooLong; \ } \ while ((len > 0) && (values->amzHeadersRaw[len - 1] == ' ')) { \ len--; \ } \ values->amzHeadersRaw[len++] = 0; \ } while (0) #define header_name_tolower_copy(str, l) \ do { \ values->amzHeaders[values->amzHeadersCount++] = \ &(values->amzHeadersRaw[len]); \ if ((len + l) >= (int) sizeof(values->amzHeadersRaw)) { \ return S3StatusMetaDataHeadersTooLong; \ } \ int todo = l; \ while (todo--) { \ if ((*(str) >= 'A') && (*(str) <= 'Z')) { \ values->amzHeadersRaw[len++] = 'a' + (*(str) - 'A'); \ } \ else { \ values->amzHeadersRaw[len++] = *(str); \ } \ (str)++; \ } \ } while (0) // Check and copy in the x-amz-meta headers if (properties) { int i; for (i = 0; i < properties->metaDataCount; i++) { const S3NameValue *property = &(properties->metaData[i]); char headerName[S3_MAX_METADATA_SIZE - sizeof(": v")]; int l = snprintf(headerName, sizeof(headerName), S3_METADATA_HEADER_NAME_PREFIX "%s", property->name); char *hn = headerName; header_name_tolower_copy(hn, l); // Copy in the value headers_append(0, ": %s", property->value); } // Add the x-amz-acl header, if necessary const char *cannedAclString; switch (params->putProperties->cannedAcl) { case S3CannedAclPrivate: cannedAclString = 0; break; case S3CannedAclPublicRead: cannedAclString = "public-read"; break; case S3CannedAclPublicReadWrite: cannedAclString = "public-read-write"; break; default: // S3CannedAclAuthenticatedRead cannedAclString = "authenticated-read"; break; } if (cannedAclString) { headers_append(1, "x-amz-acl: %s", cannedAclString); } } // Add the x-amz-date header time_t now = time(NULL); char date[64]; strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now)); headers_append(1, "x-amz-date: %s", date); if (params->httpRequestType == HttpRequestTypeCOPY) { // Add the x-amz-copy-source header if (params->copySourceBucketName && params->copySourceBucketName[0] && params->copySourceKey && params->copySourceKey[0]) { headers_append(1, "x-amz-copy-source: /%s/%s", params->copySourceBucketName, params->copySourceKey); } // And the x-amz-metadata-directive header if (params->putProperties) { headers_append(1, "%s", "x-amz-metadata-directive: REPLACE"); } } return S3StatusOK; } // Composes the other headers static S3Status compose_standard_headers(const RequestParams *params, RequestComputedValues *values) { #define do_put_header(fmt, sourceField, destField, badError, tooLongError) \ do { \ if (params->putProperties && \ params->putProperties-> sourceField && \ params->putProperties-> sourceField[0]) { \ /* Skip whitespace at beginning of val */ \ const char *val = params->putProperties-> sourceField; \ while (*val && is_blank(*val)) { \ val++; \ } \ if (!*val) { \ return badError; \ } \ /* Compose header, make sure it all fit */ \ int len = snprintf(values-> destField, \ sizeof(values-> destField), fmt, val); \ if (len >= (int) sizeof(values-> destField)) { \ return tooLongError; \ } \ /* Now remove the whitespace at the end */ \ while (is_blank(values-> destField[len])) { \ len--; \ } \ values-> destField[len] = 0; \ } \ else { \ values-> destField[0] = 0; \ } \ } while (0) #define do_get_header(fmt, sourceField, destField, badError, tooLongError) \ do { \ if (params->getConditions && \ params->getConditions-> sourceField && \ params->getConditions-> sourceField[0]) { \ /* Skip whitespace at beginning of val */ \ const char *val = params->getConditions-> sourceField; \ while (*val && is_blank(*val)) { \ val++; \ } \ if (!*val) { \ return badError; \ } \ /* Compose header, make sure it all fit */ \ int len = snprintf(values-> destField, \ sizeof(values-> destField), fmt, val); \ if (len >= (int) sizeof(values-> destField)) { \ return tooLongError; \ } \ /* Now remove the whitespace at the end */ \ while (is_blank(values-> destField[len])) { \ len--; \ } \ values-> destField[len] = 0; \ } \ else { \ values-> destField[0] = 0; \ } \ } while (0) // Cache-Control do_put_header("Cache-Control: %s", cacheControl, cacheControlHeader, S3StatusBadCacheControl, S3StatusCacheControlTooLong); // ContentType do_put_header("Content-Type: %s", contentType, contentTypeHeader, S3StatusBadContentType, S3StatusContentTypeTooLong); // MD5 do_put_header("Content-MD5: %s", md5, md5Header, S3StatusBadMD5, S3StatusMD5TooLong); // Content-Disposition do_put_header("Content-Disposition: attachment; filename=\"%s\"", contentDispositionFilename, contentDispositionHeader, S3StatusBadContentDispositionFilename, S3StatusContentDispositionFilenameTooLong); // ContentEncoding do_put_header("Content-Encoding: %s", contentEncoding, contentEncodingHeader, S3StatusBadContentEncoding, S3StatusContentEncodingTooLong); // Expires if (params->putProperties && (params->putProperties->expires >= 0)) { time_t t = (time_t) params->putProperties->expires; strftime(values->expiresHeader, sizeof(values->expiresHeader), "Expires: %a, %d %b %Y %H:%M:%S UTC", gmtime(&t)); } else { values->expiresHeader[0] = 0; } // If-Modified-Since if (params->getConditions && (params->getConditions->ifModifiedSince >= 0)) { time_t t = (time_t) params->getConditions->ifModifiedSince; strftime(values->ifModifiedSinceHeader, sizeof(values->ifModifiedSinceHeader), "If-Modified-Since: %a, %d %b %Y %H:%M:%S UTC", gmtime(&t)); } else { values->ifModifiedSinceHeader[0] = 0; } // If-Unmodified-Since header if (params->getConditions && (params->getConditions->ifNotModifiedSince >= 0)) { time_t t = (time_t) params->getConditions->ifNotModifiedSince; strftime(values->ifUnmodifiedSinceHeader, sizeof(values->ifUnmodifiedSinceHeader), "If-Unmodified-Since: %a, %d %b %Y %H:%M:%S UTC", gmtime(&t)); } else { values->ifUnmodifiedSinceHeader[0] = 0; } // If-Match header do_get_header("If-Match: %s", ifMatchETag, ifMatchHeader, S3StatusBadIfMatchETag, S3StatusIfMatchETagTooLong); // If-None-Match header do_get_header("If-None-Match: %s", ifNotMatchETag, ifNoneMatchHeader, S3StatusBadIfNotMatchETag, S3StatusIfNotMatchETagTooLong); // Range header if (params->startByte || params->byteCount) { if (params->byteCount) { snprintf(values->rangeHeader, sizeof(values->rangeHeader), "Range: bytes=%llu-%llu", (unsigned long long) params->startByte, (unsigned long long) (params->startByte + params->byteCount - 1)); } else { snprintf(values->rangeHeader, sizeof(values->rangeHeader), "Range: bytes=%llu-", (unsigned long long) params->startByte); } } else { values->rangeHeader[0] = 0; } return S3StatusOK; } // URL encodes the params->key value into params->urlEncodedKey static S3Status encode_key(const RequestParams *params, RequestComputedValues *values) { return (urlEncode(values->urlEncodedKey, params->key, S3_MAX_KEY_SIZE) ? S3StatusOK : S3StatusUriTooLong); } // Simple comparison function for comparing two HTTP header names that are // embedded within an HTTP header line, returning true if header1 comes // before header2 alphabetically, false if not static int headerle(const char *header1, const char *header2) { while (1) { if (*header1 == ':') { return (*header2 != ':'); } else if (*header2 == ':') { return 0; } else if (*header2 < *header1) { return 0; } else if (*header2 > *header1) { return 1; } header1++, header2++; } } // Replace this with merge sort eventually, it's the best stable sort. But // since typically the number of elements being sorted is small, it doesn't // matter that much which sort is used, and gnome sort is the world's simplest // stable sort. Added a slight twist to the standard gnome_sort - don't go // forward +1, go forward to the last highest index considered. This saves // all the string comparisons that would be done "going forward", and thus // only does the necessary string comparisons to move values back into their // sorted position. static void header_gnome_sort(const char **headers, int size) { int i = 0, last_highest = 0; while (i < size) { if ((i == 0) || headerle(headers[i - 1], headers[i])) { i = ++last_highest; } else { const char *tmp = headers[i]; headers[i] = headers[i - 1]; headers[--i] = tmp; } } } // Canonicalizes the x-amz- headers into the canonicalizedAmzHeaders buffer static void canonicalize_amz_headers(RequestComputedValues *values) { // Make a copy of the headers that will be sorted const char *sortedHeaders[S3_MAX_METADATA_COUNT]; memcpy(sortedHeaders, values->amzHeaders, (values->amzHeadersCount * sizeof(sortedHeaders[0]))); // Now sort these header_gnome_sort(sortedHeaders, values->amzHeadersCount); // Now copy this sorted list into the buffer, all the while: // - folding repeated headers into single lines, and // - folding multiple lines // - removing the space after the colon int lastHeaderLen = 0, i; char *buffer = values->canonicalizedAmzHeaders; for (i = 0; i < values->amzHeadersCount; i++) { const char *header = sortedHeaders[i]; const char *c = header; // If the header names are the same, append the next value if ((i > 0) && !strncmp(header, sortedHeaders[i - 1], lastHeaderLen)) { // Replacing the previous newline with a comma *(buffer - 1) = ','; // Skip the header name and space c += (lastHeaderLen + 1); } // Else this is a new header else { // Copy in everything up to the space in the ": " while (*c != ' ') { *buffer++ = *c++; } // Save the header len since it's a new header lastHeaderLen = c - header; // Skip the space c++; } // Now copy in the value, folding the lines while (*c) { // If c points to a \r\n[whitespace] sequence, then fold // this newline out if ((*c == '\r') && (*(c + 1) == '\n') && is_blank(*(c + 2))) { c += 3; while (is_blank(*c)) { c++; } // Also, what has most recently been copied into buffer amy // have been whitespace, and since we're folding whitespace // out around this newline sequence, back buffer up over // any whitespace it contains while (is_blank(*(buffer - 1))) { buffer--; } continue; } *buffer++ = *c++; } // Finally, add the newline *buffer++ = '\n'; } // Terminate the buffer *buffer = 0; } // Canonicalizes the resource into params->canonicalizedResource static void canonicalize_resource(const char *bucketName, const char *subResource, const char *urlEncodedKey, char *buffer) { int len = 0; *buffer = 0; #define append(str) len += sprintf(&(buffer[len]), "%s", str) if (bucketName && bucketName[0]) { buffer[len++] = '/'; append(bucketName); } append("/"); if (urlEncodedKey && urlEncodedKey[0]) { append(urlEncodedKey); } if (subResource && subResource[0]) { append("?"); append(subResource); } } // Convert an HttpRequestType to an HTTP Verb string static const char *http_request_type_to_verb(HttpRequestType requestType) { switch (requestType) { case HttpRequestTypeGET: return "GET"; case HttpRequestTypeHEAD: return "HEAD"; case HttpRequestTypePUT: case HttpRequestTypeCOPY: return "PUT"; default: // HttpRequestTypeDELETE return "DELETE"; } } // Composes the Authorization header for the request static S3Status compose_auth_header(const RequestParams *params, RequestComputedValues *values) { // We allow for: // 17 bytes for HTTP-Verb + \n // 129 bytes for Content-MD5 + \n // 129 bytes for Content-Type + \n // 1 byte for empty Date + \n // CanonicalizedAmzHeaders & CanonicalizedResource char signbuf[17 + 129 + 129 + 1 + (sizeof(values->canonicalizedAmzHeaders) - 1) + (sizeof(values->canonicalizedResource) - 1) + 1]; int len = 0; #define signbuf_append(format, ...) \ len += snprintf(&(signbuf[len]), sizeof(signbuf) - len, \ format, __VA_ARGS__) signbuf_append ("%s\n", http_request_type_to_verb(params->httpRequestType)); // For MD5 and Content-Type, use the value in the actual header, because // it's already been trimmed signbuf_append("%s\n", values->md5Header[0] ? &(values->md5Header[sizeof("Content-MD5: ") - 1]) : ""); signbuf_append ("%s\n", values->contentTypeHeader[0] ? &(values->contentTypeHeader[sizeof("Content-Type: ") - 1]) : ""); signbuf_append("%s", "\n"); // Date - we always use x-amz-date signbuf_append("%s", values->canonicalizedAmzHeaders); signbuf_append("%s", values->canonicalizedResource); // Generate an HMAC-SHA-1 of the signbuf unsigned char hmac[20]; HMAC_SHA1(hmac, (unsigned char *) params->bucketContext.secretAccessKey, strlen(params->bucketContext.secretAccessKey), (unsigned char *) signbuf, len); // Now base-64 encode the results char b64[((20 + 1) * 4) / 3]; int b64Len = base64Encode(hmac, 20, b64); snprintf(values->authorizationHeader, sizeof(values->authorizationHeader), "Authorization: AWS %s:%.*s", params->bucketContext.accessKeyId, b64Len, b64); return S3StatusOK; } // Compose the URI to use for the request given the request parameters static S3Status compose_uri(char *buffer, int bufferSize, const S3BucketContext *bucketContext, const char *urlEncodedKey, const char *subResource, const char *queryParams) { int len = 0; #define uri_append(fmt, ...) \ do { \ len += snprintf(&(buffer[len]), bufferSize - len, fmt, __VA_ARGS__); \ if (len >= bufferSize) { \ return S3StatusUriTooLong; \ } \ } while (0) uri_append("http%s://", (bucketContext->protocol == S3ProtocolHTTP) ? "" : "s"); const char *hostName = bucketContext->hostName ? bucketContext->hostName : defaultHostNameG; if (bucketContext->bucketName && bucketContext->bucketName[0]) { if (bucketContext->uriStyle == S3UriStyleVirtualHost) { uri_append("%s.%s", bucketContext->bucketName, hostName); } else { uri_append("%s/%s", hostName, bucketContext->bucketName); } } else { uri_append("%s", hostName); } uri_append("%s", "/"); uri_append("%s", urlEncodedKey); if (subResource && subResource[0]) { uri_append("?%s", subResource); } if (queryParams) { uri_append("%s%s", (subResource && subResource[0]) ? "&" : "?", queryParams); } return S3StatusOK; } // Sets up the curl handle given the completely computed RequestParams static S3Status setup_curl(Request *request, const RequestParams *params, const RequestComputedValues *values) { CURLcode status; #define curl_easy_setopt_safe(opt, val) \ if ((status = curl_easy_setopt \ (request->curl, opt, val)) != CURLE_OK) { \ return S3StatusFailedToInitializeRequest; \ } // Debugging only // curl_easy_setopt_safe(CURLOPT_VERBOSE, 1); // Set private data to request for the benefit of S3RequestContext curl_easy_setopt_safe(CURLOPT_PRIVATE, request); // Set header callback and data curl_easy_setopt_safe(CURLOPT_HEADERDATA, request); curl_easy_setopt_safe(CURLOPT_HEADERFUNCTION, &curl_header_func); // Set read callback, data, and readSize curl_easy_setopt_safe(CURLOPT_READFUNCTION, &curl_read_func); curl_easy_setopt_safe(CURLOPT_READDATA, request); // Set write callback and data curl_easy_setopt_safe(CURLOPT_WRITEFUNCTION, &curl_write_func); curl_easy_setopt_safe(CURLOPT_WRITEDATA, request); // Ask curl to parse the Last-Modified header. This is easier than // parsing it ourselves. curl_easy_setopt_safe(CURLOPT_FILETIME, 1); // Curl docs suggest that this is necessary for multithreaded code. // However, it also points out that DNS timeouts will not be honored // during DNS lookup, which can be worked around by using the c-ares // library, which we do not do yet. curl_easy_setopt_safe(CURLOPT_NOSIGNAL, 1); // Turn off Curl's built-in progress meter curl_easy_setopt_safe(CURLOPT_NOPROGRESS, 1); // xxx todo - support setting the proxy for Curl to use (can't use https // for proxies though) // xxx todo - support setting the network interface for Curl to use // I think this is useful - we don't need interactive performance, we need // to complete large operations quickly curl_easy_setopt_safe(CURLOPT_TCP_NODELAY, 1); // Don't use Curl's 'netrc' feature curl_easy_setopt_safe(CURLOPT_NETRC, CURL_NETRC_IGNORED); // Don't verify S3's certificate, there are known to be issues with // them sometimes // xxx todo - support an option for verifying the S3 CA (default false) curl_easy_setopt_safe(CURLOPT_SSL_VERIFYPEER, 0); // Follow any redirection directives that S3 sends curl_easy_setopt_safe(CURLOPT_FOLLOWLOCATION, 1); // A safety valve in case S3 goes bananas with redirects curl_easy_setopt_safe(CURLOPT_MAXREDIRS, 10); // Set the User-Agent; maybe Amazon will track these? curl_easy_setopt_safe(CURLOPT_USERAGENT, userAgentG); // Set the low speed limit and time; we abort transfers that stay at // less than 1K per second for more than 15 seconds. // xxx todo - make these configurable // xxx todo - allow configurable max send and receive speed curl_easy_setopt_safe(CURLOPT_LOW_SPEED_LIMIT, 1024); curl_easy_setopt_safe(CURLOPT_LOW_SPEED_TIME, 15); // Append standard headers #define append_standard_header(fieldName) \ if (values-> fieldName [0]) { \ request->headers = curl_slist_append(request->headers, \ values-> fieldName); \ } // Would use CURLOPT_INFILESIZE_LARGE, but it is buggy in libcurl if (params->httpRequestType == HttpRequestTypePUT) { char header[256]; snprintf(header, sizeof(header), "Content-Length: %llu", (unsigned long long) params->toS3CallbackTotalSize); request->headers = curl_slist_append(request->headers, header); request->headers = curl_slist_append(request->headers, "Transfer-Encoding:"); } else if (params->httpRequestType == HttpRequestTypeCOPY) { request->headers = curl_slist_append(request->headers, "Transfer-Encoding:"); } append_standard_header(cacheControlHeader); append_standard_header(contentTypeHeader); append_standard_header(md5Header); append_standard_header(contentDispositionHeader); append_standard_header(contentEncodingHeader); append_standard_header(expiresHeader); append_standard_header(ifModifiedSinceHeader); append_standard_header(ifUnmodifiedSinceHeader); append_standard_header(ifMatchHeader); append_standard_header(ifNoneMatchHeader); append_standard_header(rangeHeader); append_standard_header(authorizationHeader); // Append x-amz- headers int i; for (i = 0; i < values->amzHeadersCount; i++) { request->headers = curl_slist_append(request->headers, values->amzHeaders[i]); } // Set the HTTP headers curl_easy_setopt_safe(CURLOPT_HTTPHEADER, request->headers); // Set URI curl_easy_setopt_safe(CURLOPT_URL, request->uri); // Set request type. switch (params->httpRequestType) { case HttpRequestTypeHEAD: curl_easy_setopt_safe(CURLOPT_NOBODY, 1); break; case HttpRequestTypePUT: case HttpRequestTypeCOPY: curl_easy_setopt_safe(CURLOPT_UPLOAD, 1); break; case HttpRequestTypeDELETE: curl_easy_setopt_safe(CURLOPT_CUSTOMREQUEST, "DELETE"); break; default: // HttpRequestTypeGET break; } return S3StatusOK; } static void request_deinitialize(Request *request) { if (request->headers) { curl_slist_free_all(request->headers); } error_parser_deinitialize(&(request->errorParser)); // curl_easy_reset prevents connections from being re-used for some // reason. This makes HTTP Keep-Alive meaningless and is very bad for // performance. But it is necessary to allow curl to work properly. // xxx todo figure out why curl_easy_reset(request->curl); } static S3Status request_get(const RequestParams *params, const RequestComputedValues *values, Request **reqReturn) { Request *request = 0; // Try to get one from the request stack. We hold the lock for the // shortest time possible here. pthread_mutex_lock(&requestStackMutexG); if (requestStackCountG) { request = requestStackG[--requestStackCountG]; } pthread_mutex_unlock(&requestStackMutexG); // If we got one, deinitialize it for re-use if (request) { request_deinitialize(request); } // Else there wasn't one available in the request stack, so create one else { if (!(request = (Request *) malloc(sizeof(Request)))) { return S3StatusOutOfMemory; } if (!(request->curl = curl_easy_init())) { free(request); return S3StatusFailedToInitializeRequest; } } // Initialize the request request->prev = 0; request->next = 0; // Request status is initialized to no error, will be updated whenever // an error occurs request->status = S3StatusOK; S3Status status; // Start out with no headers request->headers = 0; // Compute the URL if ((status = compose_uri (request->uri, sizeof(request->uri), &(params->bucketContext), values->urlEncodedKey, params->subResource, params->queryParams)) != S3StatusOK) { curl_easy_cleanup(request->curl); free(request); return status; } // Set all of the curl handle options if ((status = setup_curl(request, params, values)) != S3StatusOK) { curl_easy_cleanup(request->curl); free(request); return status; } request->propertiesCallback = params->propertiesCallback; request->toS3Callback = params->toS3Callback; request->toS3CallbackBytesRemaining = params->toS3CallbackTotalSize; request->fromS3Callback = params->fromS3Callback; request->completeCallback = params->completeCallback; request->callbackData = params->callbackData; response_headers_handler_initialize(&(request->responseHeadersHandler)); request->propertiesCallbackMade = 0; error_parser_initialize(&(request->errorParser)); *reqReturn = request; return S3StatusOK; } static void request_destroy(Request *request) { request_deinitialize(request); curl_easy_cleanup(request->curl); free(request); } static void request_release(Request *request) { pthread_mutex_lock(&requestStackMutexG); // If the request stack is full, destroy this one if (requestStackCountG == REQUEST_STACK_SIZE) { pthread_mutex_unlock(&requestStackMutexG); request_destroy(request); } // Else put this one at the front of the request stack; we do this because // we want the most-recently-used curl handle to be re-used on the next // request, to maximize our chances of re-using a TCP connection before it // times out else { requestStackG[requestStackCountG++] = request; pthread_mutex_unlock(&requestStackMutexG); } } S3Status request_api_initialize(const char *userAgentInfo, int flags, const char *defaultHostName) { if (curl_global_init(CURL_GLOBAL_ALL & ~((flags & S3_INIT_WINSOCK) ? 0 : CURL_GLOBAL_WIN32)) != CURLE_OK) { return S3StatusInternalError; } if (!defaultHostName) { defaultHostName = S3_DEFAULT_HOSTNAME; } if (snprintf(defaultHostNameG, S3_MAX_HOSTNAME_SIZE, "%s", defaultHostName) >= S3_MAX_HOSTNAME_SIZE) { return S3StatusUriTooLong; } pthread_mutex_init(&requestStackMutexG, 0); requestStackCountG = 0; if (!userAgentInfo || !*userAgentInfo) { userAgentInfo = "Unknown"; } char platform[96]; struct utsname utsn; if (uname(&utsn)) { strncpy(platform, "Unknown", sizeof(platform)); // Because strncpy doesn't always zero terminate platform[sizeof(platform) - 1] = 0; } else { snprintf(platform, sizeof(platform), "%s%s%s", utsn.sysname, utsn.machine[0] ? " " : "", utsn.machine); } snprintf(userAgentG, sizeof(userAgentG), "Mozilla/4.0 (Compatible; %s; libs3 %s.%s; %s)", userAgentInfo, LIBS3_VER_MAJOR, LIBS3_VER_MINOR, platform); return S3StatusOK; } void request_api_deinitialize() { pthread_mutex_destroy(&requestStackMutexG); while (requestStackCountG--) { request_destroy(requestStackG[requestStackCountG]); } } void request_perform(const RequestParams *params, S3RequestContext *context) { Request *request; S3Status status; #define return_status(status) \ (*(params->completeCallback))(status, 0, params->callbackData); \ return // These will hold the computed values RequestComputedValues computed; // Validate the bucket name if (params->bucketContext.bucketName && ((status = S3_validate_bucket_name (params->bucketContext.bucketName, params->bucketContext.uriStyle)) != S3StatusOK)) { return_status(status); } // Compose the amz headers if ((status = compose_amz_headers(params, &computed)) != S3StatusOK) { return_status(status); } // Compose standard headers if ((status = compose_standard_headers (params, &computed)) != S3StatusOK) { return_status(status); } // URL encode the key if ((status = encode_key(params, &computed)) != S3StatusOK) { return_status(status); } // Compute the canonicalized amz headers canonicalize_amz_headers(&computed); // Compute the canonicalized resource canonicalize_resource(params->bucketContext.bucketName, params->subResource, computed.urlEncodedKey, computed.canonicalizedResource); // Compose Authorization header if ((status = compose_auth_header(params, &computed)) != S3StatusOK) { return_status(status); } // Get an initialized Request structure now if ((status = request_get(params, &computed, &request)) != S3StatusOK) { return_status(status); } // If a RequestContext was provided, add the request to the curl multi if (context) { CURLMcode code = curl_multi_add_handle(context->curlm, request->curl); if (code == CURLM_OK) { if (context->requests) { request->prev = context->requests->prev; request->next = context->requests; context->requests->prev->next = request; context->requests->prev = request; } else { context->requests = request->next = request->prev = request; } } else { if (request->status == S3StatusOK) { request->status = (code == CURLM_OUT_OF_MEMORY) ? S3StatusOutOfMemory : S3StatusInternalError; } request_finish(request); } } // Else, perform the request immediately else { CURLcode code = curl_easy_perform(request->curl); if ((code != CURLE_OK) && (request->status == S3StatusOK)) { request->status = request_curl_code_to_status(code); } // Finish the request, ensuring that all callbacks have been made, and // also releases the request request_finish(request); } } void request_finish(Request *request) { // If we haven't detected this already, we now know that the headers are // definitely done being read in request_headers_done(request); // If there was no error processing the request, then possibly there was // an S3 error parsed, which should be converted into the request status if (request->status == S3StatusOK) { error_parser_convert_status(&(request->errorParser), &(request->status)); // If there still was no error recorded, then it is possible that // there was in fact an error but that there was no error XML // detailing the error if ((request->status == S3StatusOK) && ((request->httpResponseCode < 200) || (request->httpResponseCode > 299))) { switch (request->httpResponseCode) { case 0: // This happens if the request never got any HTTP response // headers at all, we call this a ConnectionFailed error request->status = S3StatusConnectionFailed; break; case 100: // Some versions of libcurl erroneously set HTTP // status to this break; case 301: request->status = S3StatusErrorPermanentRedirect; break; case 307: request->status = S3StatusHttpErrorMovedTemporarily; break; case 400: request->status = S3StatusHttpErrorBadRequest; break; case 403: request->status = S3StatusHttpErrorForbidden; break; case 404: request->status = S3StatusHttpErrorNotFound; break; case 405: request->status = S3StatusErrorMethodNotAllowed; break; case 409: request->status = S3StatusHttpErrorConflict; break; case 411: request->status = S3StatusErrorMissingContentLength; break; case 412: request->status = S3StatusErrorPreconditionFailed; break; case 416: request->status = S3StatusErrorInvalidRange; break; case 500: request->status = S3StatusErrorInternalError; break; case 501: request->status = S3StatusErrorNotImplemented; break; case 503: request->status = S3StatusErrorSlowDown; break; default: request->status = S3StatusHttpErrorUnknown; break; } } } (*(request->completeCallback)) (request->status, &(request->errorParser.s3ErrorDetails), request->callbackData); request_release(request); } S3Status request_curl_code_to_status(CURLcode code) { switch (code) { case CURLE_OUT_OF_MEMORY: return S3StatusOutOfMemory; case CURLE_COULDNT_RESOLVE_PROXY: case CURLE_COULDNT_RESOLVE_HOST: return S3StatusNameLookupError; case CURLE_COULDNT_CONNECT: return S3StatusFailedToConnect; case CURLE_WRITE_ERROR: case CURLE_OPERATION_TIMEDOUT: return S3StatusConnectionFailed; case CURLE_PARTIAL_FILE: return S3StatusOK; case CURLE_SSL_CACERT: return S3StatusServerFailedVerification; default: return S3StatusInternalError; } } S3Status S3_generate_authenticated_query_string (char *buffer, const S3BucketContext *bucketContext, const char *key, int64_t expires, const char *resource) { #define MAX_EXPIRES (((int64_t) 1 << 31) - 1) // S3 seems to only accept expiration dates up to the number of seconds // representably by a signed 32-bit integer if (expires < 0) { expires = MAX_EXPIRES; } else if (expires > MAX_EXPIRES) { expires = MAX_EXPIRES; } // xxx todo: rework this so that it can be incorporated into shared code // with request_perform(). It's really unfortunate that this code is not // shared with request_perform(). // URL encode the key char urlEncodedKey[S3_MAX_KEY_SIZE * 3]; if (key) { urlEncode(urlEncodedKey, key, strlen(key)); } else { urlEncodedKey[0] = 0; } // Compute canonicalized resource char canonicalizedResource[MAX_CANONICALIZED_RESOURCE_SIZE]; canonicalize_resource(bucketContext->bucketName, resource, urlEncodedKey, canonicalizedResource); // We allow for: // 17 bytes for HTTP-Verb + \n // 1 byte for empty Content-MD5 + \n // 1 byte for empty Content-Type + \n // 20 bytes for Expires + \n // 0 bytes for CanonicalizedAmzHeaders // CanonicalizedResource char signbuf[17 + 1 + 1 + 1 + 20 + sizeof(canonicalizedResource) + 1]; int len = 0; #define signbuf_append(format, ...) \ len += snprintf(&(signbuf[len]), sizeof(signbuf) - len, \ format, __VA_ARGS__) signbuf_append("%s\n", "GET"); // HTTP-Verb signbuf_append("%s\n", ""); // Content-MD5 signbuf_append("%s\n", ""); // Content-Type signbuf_append("%llu\n", (unsigned long long) expires); signbuf_append("%s", canonicalizedResource); // Generate an HMAC-SHA-1 of the signbuf unsigned char hmac[20]; HMAC_SHA1(hmac, (unsigned char *) bucketContext->secretAccessKey, strlen(bucketContext->secretAccessKey), (unsigned char *) signbuf, len); // Now base-64 encode the results char b64[((20 + 1) * 4) / 3]; int b64Len = base64Encode(hmac, 20, b64); // Now urlEncode that char signature[sizeof(b64) * 3]; urlEncode(signature, b64, b64Len); // Finally, compose the uri, with params: // ?AWSAccessKeyId=xxx[&Expires=]&Signature=xxx char queryParams[sizeof("AWSAccessKeyId=") + 20 + sizeof("&Expires=") + 20 + sizeof("&Signature=") + sizeof(signature) + 1]; sprintf(queryParams, "AWSAccessKeyId=%s&Expires=%ld&Signature=%s", bucketContext->accessKeyId, (long) expires, signature); return compose_uri(buffer, S3_MAX_AUTHENTICATED_QUERY_STRING_SIZE, bucketContext, urlEncodedKey, resource, queryParams); } libs3-2.0/ChangeLog0000644000175000001440000000121511640553711012626 0ustar bjiusersThu Sep 18 10:03:02 NZST 2008 bryan@ischo.com * This file is no longer maintained, sorry Sat Aug 9 13:44:21 NZST 2008 bryan@ischo.com * Fixed bug wherein keys with non-URI-safe characters did not work correctly because they were not being URI-encoded in the request UR * Split RPM and DEB packages into normal and devel packages Fri Aug 8 22:40:19 NZST 2008 bryan@ischo.com * Branched 0.4 * Created RPM and Debian packaging Tue Aug 5 08:52:33 NZST 2008 bryan@ischo.com * Bumped version number to 0.3 * Moved Makefile to GNUmakefile, added shared library build * Added a bunch of GNU standard files (README, INSTALL, ChangeLog, etc) libs3-2.0/LICENSE0000644000175000001440000000150211640553711012060 0ustar bjiusersCopyright 2008 Bryan Ischo libs3 is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. In addition, as a special exception, the copyright holders give permission to link the code of this library and its programs with the OpenSSL library, and distribute linked combinations including the two. libs3 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License version 3 along with libs3, in a file named COPYING. If not, see . libs3-2.0/COPYING0000644000175000001440000010451311640553711012114 0ustar bjiusers GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read .