Merge pull request #201 from coreos/godep-to-glide
vendor: Switch from godep to glide tool
@@ -23,7 +23,7 @@ Alternately, build a Docker image `coreos/bootcfg:latest`.
|
||||
|
||||
sudo ./build-docker
|
||||
|
||||
## Check Version
|
||||
## Version
|
||||
|
||||
./bin/bootcfg -version
|
||||
sudo rkt --insecure-options=image run bootcfg.aci -- -version
|
||||
@@ -41,4 +41,19 @@ Run the ACI with rkt on `metal0`.
|
||||
|
||||
Alternately, run the Docker image on `docker0`.
|
||||
|
||||
sudo docker run -p 8080:8080 --rm -v $PWD/examples:/var/lib/bootcfg:Z -v $PWD/examples/groups/etcd-docker:/var/lib/bootcfg/groups:Z coreos/bootcfg:latest -address=0.0.0.0:8080 -log-level=debug
|
||||
sudo docker run -p 8080:8080 --rm -v $PWD/examples:/var/lib/bootcfg:Z -v $PWD/examples/groups/etcd-docker:/var/lib/bootcfg/groups:Z coreos/bootcfg:latest -address=0.0.0.0:8080 -log-level=debug
|
||||
|
||||
## Dependencies
|
||||
|
||||
Project dependencies are commited to the `vendor` directory, so Go 1.6+ users can clone to their `GOPATH` and build or test immediately. Go 1.5 users should set `GO15VENDOREXPERIMENT=1`.
|
||||
|
||||
Project developers should use [glide](https://github.com/Masterminds/glide) to manage commited dependencies under `vendor`. Configure `glide.yaml` as desired. Use `glide update` to download and update dependencies listed in `glide.yaml` into `/vendor` (do **not** use glide `get`).
|
||||
|
||||
glide update --update-vendored --strip-vendor --strip-vcs
|
||||
|
||||
Recursive dependencies are also vendored. A `glide.lock` will be created to represent the exact versions of each dependency.
|
||||
|
||||
With an empty `vendor` directory, you can install the `glide.lock` dependencies.
|
||||
|
||||
rm -rf vendor/
|
||||
glide install --strip-vendor --strip-vcs
|
||||
|
||||
95
glide.lock
generated
Normal file
@@ -0,0 +1,95 @@
|
||||
hash: 8f33fd1c87e2136cdff69364e0668a595a129b8ffd686851336c841bf7e4f705
|
||||
updated: 2016-05-12T14:04:55.653773498-07:00
|
||||
imports:
|
||||
- name: github.com/alecthomas/units
|
||||
version: 2efee857e7cfd4f3d0138cc3cbb1b4966962b93a
|
||||
- name: github.com/camlistore/camlistore
|
||||
version: 9106ce829629773474c689b34aacd7d3aaa99426
|
||||
subpackages:
|
||||
- pkg/errorutil
|
||||
- name: github.com/coreos/coreos-cloudinit
|
||||
version: b3f805dee6a4aa5ed298a1f370284df470eecf43
|
||||
subpackages:
|
||||
- config
|
||||
- name: github.com/coreos/go-semver
|
||||
version: 294930c1e79c64e7dbe360054274fdad492c8cf5
|
||||
subpackages:
|
||||
- semver
|
||||
- name: github.com/coreos/go-systemd
|
||||
version: 7b2428fec40033549c68f54e26e89e7ca9a9ce31
|
||||
subpackages:
|
||||
- journal
|
||||
- name: github.com/coreos/ignition
|
||||
version: 44c274ab414294a8e34b3a940e0ec1afe6b6c610
|
||||
subpackages:
|
||||
- config
|
||||
- config/types
|
||||
- config/v1
|
||||
- config/v1/types
|
||||
- name: github.com/coreos/pkg
|
||||
version: 66fe44ad037ccb80329115cb4db0dbe8e9beb03a
|
||||
subpackages:
|
||||
- capnslog
|
||||
- flagutil
|
||||
- name: github.com/coreos/yaml
|
||||
version: 6b16a5714269b2f70720a45406b1babd947a17ef
|
||||
- name: github.com/davecgh/go-spew
|
||||
version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
|
||||
subpackages:
|
||||
- spew
|
||||
- name: github.com/golang/protobuf
|
||||
version: f0a097ddac24fb00e07d2ac17f8671423f3ea47c
|
||||
subpackages:
|
||||
- proto
|
||||
- name: github.com/inconshreveable/mousetrap
|
||||
version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
||||
- name: github.com/pmezard/go-difflib
|
||||
version: 792786c7400a136282c1664665ae0a8db921c6c2
|
||||
subpackages:
|
||||
- difflib
|
||||
- name: github.com/spf13/cobra
|
||||
version: 65a708cee0a4424f4e353d031ce440643e312f92
|
||||
- name: github.com/spf13/pflag
|
||||
version: 7f60f83a2c81bc3c3c0d5297f61ddfa68da9d3b7
|
||||
- name: github.com/stretchr/testify
|
||||
version: 1f4a1643a57e798696635ea4c126e9127adb7d3c
|
||||
subpackages:
|
||||
- assert
|
||||
- name: github.com/vincent-petithory/dataurl
|
||||
version: 9a301d65acbb728fcc3ace14f45f511a4cfeea9c
|
||||
- name: go4.org
|
||||
version: 03efcb870d84809319ea509714dd6d19a1498483
|
||||
subpackages:
|
||||
- errorutil
|
||||
- name: golang.org/x/crypto
|
||||
version: 5dc8cb4b8a8eb076cbb5a06bc3b8682c15bdbbd3
|
||||
subpackages:
|
||||
- cast5
|
||||
- openpgp
|
||||
- openpgp/armor
|
||||
- openpgp/errors
|
||||
- openpgp/packet
|
||||
- openpgp/s2k
|
||||
- openpgp/elgamal
|
||||
- name: golang.org/x/net
|
||||
version: fb93926129b8ec0056f2f458b1f519654814edf0
|
||||
subpackages:
|
||||
- context
|
||||
- http2
|
||||
- internal/timeseries
|
||||
- trace
|
||||
- http2/hpack
|
||||
- name: google.golang.org/grpc
|
||||
version: 8eeecf2291de9d171d0b1392a27ff3975679f4f5
|
||||
subpackages:
|
||||
- codes
|
||||
- credentials
|
||||
- grpclog
|
||||
- internal
|
||||
- metadata
|
||||
- naming
|
||||
- transport
|
||||
- peer
|
||||
- name: gopkg.in/yaml.v2
|
||||
version: f7716cbe52baa25d2e9b0d0da546fcf909fc16b4
|
||||
devImports: []
|
||||
77
glide.yaml
Normal file
@@ -0,0 +1,77 @@
|
||||
package: github.com/coreos/coreos-baremetal
|
||||
import:
|
||||
- package: github.com/alecthomas/units
|
||||
version: 2efee857e7cfd4f3d0138cc3cbb1b4966962b93a
|
||||
- package: github.com/camlistore/camlistore
|
||||
version: 9106ce829629773474c689b34aacd7d3aaa99426
|
||||
- package: github.com/coreos/coreos-cloudinit
|
||||
version: b3f805dee6a4aa5ed298a1f370284df470eecf43
|
||||
subpackages:
|
||||
- Godeps/_workspace/src/github.com/coreos/yaml
|
||||
- config
|
||||
- package: github.com/coreos/go-semver
|
||||
version: 294930c1e79c64e7dbe360054274fdad492c8cf5
|
||||
subpackages:
|
||||
- semver
|
||||
- package: github.com/coreos/go-systemd
|
||||
version: 7b2428fec40033549c68f54e26e89e7ca9a9ce31
|
||||
subpackages:
|
||||
- journal
|
||||
- package: github.com/coreos/ignition
|
||||
version: 44c274ab414294a8e34b3a940e0ec1afe6b6c610
|
||||
subpackages:
|
||||
- config
|
||||
- config/types
|
||||
- config/v1
|
||||
- config/v1/types
|
||||
- package: github.com/coreos/pkg
|
||||
version: 66fe44ad037ccb80329115cb4db0dbe8e9beb03a
|
||||
subpackages:
|
||||
- capnslog
|
||||
- flagutil
|
||||
- package: github.com/davecgh/go-spew
|
||||
version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
|
||||
subpackages:
|
||||
- spew
|
||||
- package: github.com/golang/protobuf
|
||||
version: f0a097ddac24fb00e07d2ac17f8671423f3ea47c
|
||||
subpackages:
|
||||
- proto
|
||||
- package: github.com/pmezard/go-difflib
|
||||
version: 792786c7400a136282c1664665ae0a8db921c6c2
|
||||
subpackages:
|
||||
- difflib
|
||||
- package: github.com/spf13/cobra
|
||||
version: 65a708cee0a4424f4e353d031ce440643e312f92
|
||||
- package: github.com/spf13/pflag
|
||||
version: 7f60f83a2c81bc3c3c0d5297f61ddfa68da9d3b7
|
||||
- package: github.com/stretchr/testify
|
||||
version: 1f4a1643a57e798696635ea4c126e9127adb7d3c
|
||||
subpackages:
|
||||
- assert
|
||||
- package: github.com/vincent-petithory/dataurl
|
||||
version: 9a301d65acbb728fcc3ace14f45f511a4cfeea9c
|
||||
- package: go4.org
|
||||
version: 03efcb870d84809319ea509714dd6d19a1498483
|
||||
subpackages:
|
||||
- errorutil
|
||||
- package: golang.org/x/crypto
|
||||
version: 5dc8cb4b8a8eb076cbb5a06bc3b8682c15bdbbd3
|
||||
subpackages:
|
||||
- cast5
|
||||
- openpgp
|
||||
- package: golang.org/x/net
|
||||
version: fb93926129b8ec0056f2f458b1f519654814edf0
|
||||
subpackages:
|
||||
- context
|
||||
- http2
|
||||
- internal/timeseries
|
||||
- trace
|
||||
- package: google.golang.org/grpc
|
||||
version: 8eeecf2291de9d171d0b1392a27ff3975679f4f5
|
||||
subpackages:
|
||||
- codes
|
||||
- package: gopkg.in/yaml.v2
|
||||
version: f7716cbe52baa25d2e9b0d0da546fcf909fc16b4
|
||||
- package: github.com/coreos/yaml
|
||||
version: 6b16a5714269b2f70720a45406b1babd947a17ef
|
||||
34
vendor/github.com/camlistore/camlistore/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
*~
|
||||
*.o
|
||||
*.pyc
|
||||
\#*\#
|
||||
.\#*
|
||||
.*.swp
|
||||
logs
|
||||
_obj
|
||||
[68].out
|
||||
_test
|
||||
_gotest*
|
||||
_testmain*
|
||||
_go_.[568]
|
||||
_cgo*
|
||||
clients/go/camgsinit/camgsinit
|
||||
clients/go/camwebdav/camwebdav
|
||||
.goroot
|
||||
appengine-sdk
|
||||
build/root
|
||||
.DS_Store
|
||||
bin/cam*
|
||||
bin/devcam
|
||||
bin/*_*
|
||||
bin/hello
|
||||
bin/publisher
|
||||
tmp
|
||||
server/camlistored/newui/all.js
|
||||
server/camlistored/newui/all.js.map
|
||||
server/camlistored/newui/zembed_all.js.go
|
||||
server/appengine/source_root/
|
||||
config/tls.*
|
||||
misc/docker/djpeg-static/djpeg
|
||||
misc/docker/camlistored/camlistored*
|
||||
misc/docker/camlistored/djpeg
|
||||
23
vendor/github.com/camlistore/camlistore/.hackfests/2010-12-01.txt
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
Brett and I eating Burritos from Little Chihuahua at my house.
|
||||
|
||||
Plan is for Brett to work on Clip-it-Good (the Chrome Extension)'s
|
||||
camli support (currently non-existent), and get it to:
|
||||
|
||||
-- select an image
|
||||
-- upload the image blob
|
||||
-- create the permanode blob
|
||||
-- create (and sign, with the signing server) the "become" claim,
|
||||
pointing the permanode at the image
|
||||
-- create (a signed) "tag" blob, tagging an image e.g. "funny"
|
||||
|
||||
I will work on docs & signing server tests & signing verification
|
||||
endpoint.
|
||||
|
||||
|
||||
--------------
|
||||
|
||||
Done:
|
||||
|
||||
* Brad: docs re-organized
|
||||
* Brad: camlistore.{com,org,net,info,us} domains purchased
|
||||
|
||||
4
vendor/github.com/camlistore/camlistore/.hackfests/2012-11-03.txt
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
Saturday & Sunday in Paris with Mathieu, meeting for the first time,
|
||||
hacking on EXIF rotation, thumbnail indexing, Postgres support, and
|
||||
then Monday at Google Paris, working on different parts of the UI
|
||||
permanode thumbnail page, and genfileembed problems.
|
||||
1
vendor/github.com/camlistore/camlistore/.hackfests/2012-12-23.txt
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Closure newui hacking with bslatkin.
|
||||
5
vendor/github.com/camlistore/camlistore/.hackfests/2013-01-20.txt
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
At Brett's place, with Brett Slatkin, Lindsey Simon, Ryan Barrett.
|
||||
|
||||
Goal: More closure UI stuff.
|
||||
|
||||
Ryan getting up-to-speed and maybe working on Activity Streams import.
|
||||
8
vendor/github.com/camlistore/camlistore/.hackfests/2013-12-27.txt
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
Aaron Boodman, react js permanode UI
|
||||
Andy Smith, discussing data model, updating HACKING, etc
|
||||
Brad Fitzpatrick, misc
|
||||
Brett Slatkin, web screenshotting client app, claim creation API/ACLs
|
||||
Daisy Stanton, her own thing
|
||||
Dan Erat, music player app
|
||||
Emil Eklund, getting up to speed, photo stuff
|
||||
Nick O'Neill, iOS
|
||||
20
vendor/github.com/camlistore/camlistore/.header
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
Copyright 2015 The Camlistore Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package x
|
||||
|
||||
import (
|
||||
)
|
||||
65
vendor/github.com/camlistore/camlistore/AUTHORS
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
# This is the official list of Camlistore authors for copyright purposes.
|
||||
# This file is distinct from the CONTRIBUTORS files.
|
||||
# See the latter for an explanation.
|
||||
|
||||
Aaron Bieber <deftly@gmail.com>
|
||||
Alessandro Arzilli <alessandro.arzilli@gmail.com> gh=aarzilli
|
||||
Amir Mohammad Saied <amirsaied@gmail.com>
|
||||
Amit Levy <aalevy@gmail.com>
|
||||
Andy Smith <github@anarkystic.com>
|
||||
Anthony Martin <ality@pbrane.org>
|
||||
Antonin Amand <antonin.amand@gmail.com>
|
||||
Antti Rasinen <ars@iki.fi>
|
||||
Armen Baghumian <armen@vardump.org>
|
||||
Bret Comnes <bcomnes@gmail.com>
|
||||
Brian Marete <marete@toshnix.com>
|
||||
Caine Tighe <arctanofyourface@gmail.com>
|
||||
Dan Kortschak <dan.kortschak@adelaide.edu.au>
|
||||
Daniel Coonce <danielcoonce@gmail.com>
|
||||
Daniel Dermott Bryan <danbryan@gmail.com>
|
||||
Daniel Pupius <dan.pupius@gmail.com>
|
||||
Dean Landolt <dean@deanlandolt.com>
|
||||
Dustin Sallings <dsallings@gmail.com>
|
||||
Edward Sheffler III <ed@flin.ch>
|
||||
Emil Hessman <emil@hessman.se>
|
||||
Eric Drechsel <eric@pdxhub.org>
|
||||
Fabian Wickborn <fabian@wickborn.net>
|
||||
Gina White <ginabythebay@gmail.com>
|
||||
Google Inc.
|
||||
Govert Versluis <govert@ver.slu.is>
|
||||
Hernan Grecco <hernan.grecco@gmail.com>
|
||||
Iain Peet <iain.peet@gmail.com> <iain@ipeet.org>
|
||||
Jakub Brzeski <jk.brzeski@gmail.com>
|
||||
Jani Monoses <jani@ubuntu.com>
|
||||
Jingguo Yao <yaojingguo@gmail.com>
|
||||
Josh Bleecher Snyder <josharian@gmail.com>
|
||||
Josh Huckabee <joshhuckabee@gmail.com>
|
||||
Joshua Gay <joshuagay@gmail.com>
|
||||
Jrabbit <jackjrabbit@gmail.com>
|
||||
Julien Danjou <julien@danjou.info>
|
||||
Kamil Kisiel <kamil@kamilkisiel.net> <kamil.kisiel@gmail.com>
|
||||
Kristopher Cost <me@krisco.st>
|
||||
Lindsey Simon <lsimon@commoner.com>
|
||||
Mario Russo <mail.mr@gmail.com>
|
||||
Mateus Braga <mateus.a.braga@gmail.com>
|
||||
Mathieu Lonjaret <mathieu.lonjaret@gmail.com>
|
||||
Matthieu Rakotojaona Rainimangavelo <matthieu.rakotojaona@gmail.com>
|
||||
Matt Jibson <matt.jibson@gmail.com>
|
||||
Maxime Lavigne <duguigne@gmail.com>
|
||||
Michael Vincent Zuffoletti <mikezuff@gmail.com>
|
||||
Nick O'Neill <nick.oneill@gmail.com>
|
||||
Nolan Darilek <nolan@thewordnerd.info>
|
||||
Philio <phil@bayfmail.com>
|
||||
Piotr Staszewski <p.staszewski@gmail.com>
|
||||
Ranveer <ranveerkunal@gmail.com>
|
||||
Ritesh Sinha <ritesh.kumar.sinha@gmail.com>
|
||||
Rob Young <bubblenut@gmail.com>
|
||||
Robert Obryk <robryk@gmail.com>
|
||||
Robert Hencke <robert.hencke@gmail.com>
|
||||
Salman Aljammaz <s@0x65.net>
|
||||
Sarath Lakshman <sarathlakshman@slynux.com>
|
||||
Steve Phillips <elimisteve@gmail.com>
|
||||
Steven L. Speek <slspeek@gmail.com>
|
||||
Tamás Gulácsi <tgulacsi78@gmail.com>
|
||||
Timo Truyts <alkaloid.btx@gmail.com>
|
||||
Ulf Holm Nielsen <doktor@dyregod.dk>
|
||||
12
vendor/github.com/camlistore/camlistore/BUILDING
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
To build Camlistore:
|
||||
|
||||
1) Install Go 1.5 or later.
|
||||
|
||||
2) cd to the root of the Camlistore source (where this file is)
|
||||
|
||||
3) Run:
|
||||
|
||||
$ go run make.go
|
||||
|
||||
4) The compiled binaries should now be in the "bin" subdirectory:
|
||||
camlistored (the server), camget, camput, and camtool.
|
||||
93
vendor/github.com/camlistore/camlistore/CONTRIBUTORS
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
# People who have agreed to one of the CLAs and can contribute patches.
|
||||
# The AUTHORS file lists the copyright holders; this file
|
||||
# lists people. For example, Google employees are listed here
|
||||
# but not in AUTHORS, because Google holds the copyright.
|
||||
#
|
||||
# http://code.google.com/legal/individual-cla-v1.0.html (electronic submission)
|
||||
# http://code.google.com/legal/corporate-cla-v1.0.html (requires FAX)
|
||||
#
|
||||
# Note that the CLA isn't a copyright _assignment_ but rather a
|
||||
# copyright _license_. You retain the copyright on your
|
||||
# contributions.
|
||||
|
||||
Aaron Bieber <deftly@gmail.com>
|
||||
Aaron Boodman <aaron@aaronboodman.com>
|
||||
Aaron Racine <aracine@google.com>
|
||||
Adam Langley <agl@golang.org>
|
||||
Alessandro Arzilli <alessandro.arzilli@gmail.com> gh=aarzilli
|
||||
Ali Afshar <afshar@google.com>
|
||||
Amir Mohammad Saied <amirsaied@gmail.com>
|
||||
Amit Levy <aalevy@gmail.com>
|
||||
Andrew Gerrand <adg@golang.org>
|
||||
Andy Smith <github@anarkystic.com>
|
||||
Anthony Martin <ality@pbrane.org>
|
||||
Antonin Amand <antonin.amand@gmail.com>
|
||||
Antti Rasinen <ars@iki.fi>
|
||||
Armen Baghumian <armen@vardump.org>
|
||||
Bill Thiede <couchmoney@gmail.com>
|
||||
Brad Fitzpatrick <brad@danga.com>
|
||||
Bret Comnes <bcomnes@gmail.com>
|
||||
Brett Slatkin <bslatkin@gmail.com>
|
||||
Brian Marete <marete@toshnix.com>
|
||||
Burcu Dogan <jbd@google.com>
|
||||
Caine Tighe <arctanofyourface@gmail.com>
|
||||
Dan Kortschak <dan.kortschak@adelaide.edu.au>
|
||||
Daniel Coonce <danielcoonce@gmail.com>
|
||||
Daniel Dermott Bryan <danbryan@gmail.com>
|
||||
Daniel Erat <dan@erat.org>
|
||||
Daniel Pupius <dan.pupius@gmail.com>
|
||||
Dean Landolt <dean@deanlandolt.com>
|
||||
Dustin Sallings <dsallings@gmail.com>
|
||||
Edward Sheffler III <ed@flin.ch>
|
||||
Emil Hessman <emil@hessman.se>
|
||||
Eric Drechsel <eric@pdxhub.org>
|
||||
Evan Martin <martine@danga.com>
|
||||
Fabian Wickborn <fabian@wickborn.net>
|
||||
Gina White <ginabythebay@gmail.com>
|
||||
Govert Versluis <govert@ver.slu.is>
|
||||
Han-Wen Nienhuys <hanwen@google.com>
|
||||
Hernan Grecco <hernan.grecco@gmail.com>
|
||||
Iain Peet <iain.peet@gmail.com> <iain@ipeet.org>
|
||||
Jakub Brzeski <jk.brzeski@gmail.com>
|
||||
Jani Monoses <jani@ubuntu.com>
|
||||
Jingguo Yao <yaojingguo@gmail.com>
|
||||
Johan Euphrosine <proppy@google.com>
|
||||
Josh Bleecher Snyder <josharian@gmail.com>
|
||||
Josh Huckabee <joshhuckabee@gmail.com>
|
||||
Joshua Gay <joshuagay@gmail.com>
|
||||
Jrabbit <jackjrabbit@gmail.com>
|
||||
Julien Danjou <julien@danjou.info>
|
||||
Kamil Kisiel <kamil@kamilkisiel.net> <kamil.kisiel@gmail.com>
|
||||
Kristopher Cost <me@krisco.st>
|
||||
Lindsey Simon <lsimon@commoner.com>
|
||||
Marc-Antoine Ruel <maruel@chromium.org>
|
||||
Mario Russo <mail.mr@gmail.com>
|
||||
Mateus Braga <mateus.a.braga@gmail.com>
|
||||
Mathieu Lonjaret <mathieu.lonjaret@gmail.com>
|
||||
Matthieu Rakotojaona Rainimangavelo <matthieu.rakotojaona@gmail.com>
|
||||
Matt Jibson <matt.jibson@gmail.com>
|
||||
Maxime Lavigne <duguigne@gmail.com>
|
||||
Michael Vincent Zuffoletti <mikezuff@gmail.com>
|
||||
Nick O'Neill <nick.oneill@gmail.com>
|
||||
Nico Weber <thakis@chromium.org>
|
||||
Nigel Tao <nigeltao@golang.org>
|
||||
Nolan Darilek <nolan@thewordnerd.info>
|
||||
Pawel Szczur <pawelszczur@gmail.com>
|
||||
Philio <phil@bayfmail.com>
|
||||
Piotr Staszewski <p.staszewski@gmail.com>
|
||||
Ranveer <ranveerkunal@gmail.com>
|
||||
Ritesh Sinha <ritesh.kumar.sinha@gmail.com>
|
||||
Rob Young <bubblenut@gmail.com>
|
||||
Robert Hencke <robert.hencke@gmail.com>
|
||||
Robert Kroeger <rjkroege@liqui.org>
|
||||
Robert Obryk <robryk@gmail.com>
|
||||
Ryan Barrett <camlistore@ryanb.org>
|
||||
Salman Aljammaz <s@0x65.net>
|
||||
Sarath Lakshman <sarathlakshman@slynux.com>
|
||||
Steve Phillips <elimisteve@gmail.com>
|
||||
Steven L. Speek <slspeek@gmail.com>
|
||||
Tamás Gulácsi <tgulacsi78@gmail.com>
|
||||
Timo Truyts <alkaloid.btx@gmail.com>
|
||||
Tony Chang <tony@ponderer.org>
|
||||
Tony Scelfo <scelfo@tonyscelfo.com>
|
||||
Ulf Holm Nielsen <doktor@dyregod.dk>
|
||||
56
vendor/github.com/camlistore/camlistore/Dockerfile
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
# Build everything at least. This is a work in progress.
|
||||
#
|
||||
# Useful for testing things before a release.
|
||||
#
|
||||
# Will also be used for running the camlistore.org website and public
|
||||
# read-only blobserver.
|
||||
|
||||
FROM ubuntu:12.04
|
||||
|
||||
MAINTAINER camlistore <camlistore@googlegroups.com>
|
||||
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
RUN apt-get update && apt-get upgrade -y
|
||||
RUN apt-get install -y curl make git
|
||||
|
||||
RUN curl -o /tmp/go.tar.gz https://storage.googleapis.com/golang/go1.3.1.linux-amd64.tar.gz
|
||||
RUN tar -C /usr/local -zxvf /tmp/go.tar.gz
|
||||
RUN rm /tmp/go.tar.gz
|
||||
RUN /usr/local/go/bin/go version
|
||||
|
||||
ENV GOROOT /usr/local/go
|
||||
ENV PATH $GOROOT/bin:/gopath/bin:$PATH
|
||||
|
||||
RUN mkdir -p /gopath/src
|
||||
ADD pkg /gopath/src/camlistore.org/pkg
|
||||
ADD cmd /gopath/src/camlistore.org/cmd
|
||||
ADD website /gopath/src/camlistore.org/website
|
||||
ADD third_party /gopath/src/camlistore.org/third_party
|
||||
ADD server /gopath/src/camlistore.org/server
|
||||
ADD dev /gopath/src/camlistore.org/dev
|
||||
ADD depcheck /gopath/src/camlistore.org/depcheck
|
||||
|
||||
RUN adduser --disabled-password --quiet --gecos Camli camli
|
||||
RUN mkdir -p /gopath/bin
|
||||
RUN chown camli.camli /gopath/bin
|
||||
RUN mkdir -p /gopath/pkg
|
||||
RUN chown camli.camli /gopath/pkg
|
||||
USER camli
|
||||
|
||||
ENV GOPATH /gopath
|
||||
|
||||
RUN go install --tags=purego \
|
||||
camlistore.org/server/camlistored \
|
||||
camlistore.org/cmd/camput \
|
||||
camlistore.org/cmd/camget \
|
||||
camlistore.org/cmd/camtool \
|
||||
camlistore.org/website \
|
||||
camlistore.org/dev/devcam
|
||||
|
||||
ENV USER camli
|
||||
ENV HOME /home/camli
|
||||
WORKDIR /home/camli
|
||||
|
||||
EXPOSE 80 443 3179 8080
|
||||
|
||||
CMD /bin/bash
|
||||
110
vendor/github.com/camlistore/camlistore/HACKING
generated
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
Camlistore contributors regularly use Linux and OS X, and both are
|
||||
100% supported.
|
||||
|
||||
Developing on Windows is sometimes broken, but should work. Let us
|
||||
know if we broke something, or we accidentally depend on some
|
||||
Unix-specific build tool somewhere.
|
||||
|
||||
See http://camlistore.org/docs/contributing for information on how to
|
||||
contribute to the project and submit patches. Notably, we use Gerrit
|
||||
for code review. Our Gerrit instance is at https://camlistore.org/r/
|
||||
|
||||
See architecture docs: https://camlistore.org/docs/
|
||||
|
||||
You can view docs for Camlistore packages with local godoc, or
|
||||
godoc.org.
|
||||
|
||||
It's recommended you use git to fetch the source code, rather than
|
||||
hack from a Camlistore release's zip file:
|
||||
|
||||
$ git clone https://camlistore.googlesource.com/camlistore
|
||||
|
||||
(We use github for distribution but its code review system is so poor,
|
||||
we don't use its Pull Request mechanism. The Gerrit git server & code
|
||||
review system is the main repo. See
|
||||
http://camlistore.org/docs/contributing for how to use them. We might
|
||||
support github for pull requests in the future, once it's properly
|
||||
integrated with external code review tools. We had a meeting with Github
|
||||
to discuss the ways in which their code review tools are poor.)
|
||||
|
||||
On Debian/Ubuntu, some deps to get started:
|
||||
|
||||
$ sudo apt-get install libsqlite3-dev sqlite3 pkg-config git
|
||||
|
||||
During development, rather than use the main binaries ("camput",
|
||||
"camget", "camtool", "cammount", etc) directly, we instead use a
|
||||
wrapper (devcam) that automatically configure the environment to use
|
||||
the test server & test environment.
|
||||
|
||||
To build devcam:
|
||||
|
||||
$ go run make.go
|
||||
|
||||
And devcam will be in <camroot>/bin/devcam. You'll probably want to
|
||||
symlink it into your $PATH.
|
||||
|
||||
Alternatively, if your Camlistore root is checked out at
|
||||
$GOPATH/src/camlistore.org (optional, but natural for Go users), you
|
||||
can just:
|
||||
|
||||
$ export GO15VENDOREXPERIMENT=1 # required for all Camlistore builds
|
||||
$ go install ./dev/devcam
|
||||
|
||||
The subcommands of devcam start the server or run camput/camget/etc:
|
||||
|
||||
$ devcam server # main server
|
||||
$ devcam appengine # App Engine version of the server
|
||||
$ devcam put # camput
|
||||
$ devcam get # camget
|
||||
$ devcam tool # camtool
|
||||
$ devcam mount # cammount
|
||||
|
||||
Once the dev server is running,
|
||||
|
||||
- Upload a file:
|
||||
devcam put file ~/camlistore/COPYING
|
||||
- Create a permanode:
|
||||
devcam put permanode
|
||||
- Use the UI: http://localhost:3179/ui/
|
||||
|
||||
Before submitting a patch, you should check that all the tests pass with:
|
||||
|
||||
$ devcam test
|
||||
|
||||
You can use your usual git workflow to commit your changes, but for each
|
||||
change to be reviewed you should merge your commits into one before submitting
|
||||
to gerrit for review.
|
||||
|
||||
You should also try to write a meaningful commit message, which at least states
|
||||
in the first sentence what part or package of camlistore this commit is affecting.
|
||||
The following text should state what problem the change is addressing, and how.
|
||||
Finally, you should refer to the github issue(s) the commit is addressing, if any,
|
||||
and with the appropriate keyword if the commit is fixing the issue. (See
|
||||
https://help.github.com/articles/closing-issues-via-commit-messages/).
|
||||
|
||||
For example:
|
||||
|
||||
"
|
||||
pkg/search: add "file" predicate to search by file name
|
||||
|
||||
File names were already indexed but there was no way to query the index for a file
|
||||
by its name. The "file" predicate can now be used in search expressions (e.g. in the
|
||||
search box of the web user interface) to achieve that.
|
||||
|
||||
Fixes #10987
|
||||
"
|
||||
|
||||
If your commit is adding or updating a vendored third party, you must indicate
|
||||
in your commit message the version (e.g. git commit hash) of said third party.
|
||||
|
||||
You can optionally use our pre-commit hook so that your code gets gofmt'ed
|
||||
before being submitted (which should be done anyway).
|
||||
|
||||
$ cd .git/hooks
|
||||
$ ln -s ../../misc/pre-commit.githook pre-commit
|
||||
|
||||
Finally, submit your code to gerrit with:
|
||||
|
||||
$ devcam review
|
||||
|
||||
Please update this file as appropriate.
|
||||
36
vendor/github.com/camlistore/camlistore/Makefile
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
# The normal way to build Camlistore is just "go run make.go", which
|
||||
# works everywhere, even on systems without Make. The rest of this
|
||||
# Makefile is mostly historical and should hopefully disappear over
|
||||
# time.
|
||||
all:
|
||||
go run make.go
|
||||
|
||||
# On OS X with "brew install sqlite3", you need PKG_CONFIG_PATH=/usr/local/Cellar/sqlite/3.7.17/lib/pkgconfig/
|
||||
full:
|
||||
go install --ldflags="-X camlistore.org/pkg/buildinfo.GitInfo "`./misc/gitversion` `pkg-config --libs sqlite3 1>/dev/null 2>/dev/null && echo "--tags=with_sqlite"` ./pkg/... ./server/... ./cmd/... ./third_party/... ./dev/...
|
||||
|
||||
|
||||
# Workaround Go bug where the $GOPATH/pkg cache doesn't know about tag changes.
|
||||
# Useful when you accidentally run "make" and then "make presubmit" doesn't work.
|
||||
# See https://code.google.com/p/go/issues/detail?id=4443
|
||||
forcefull:
|
||||
go install -a --tags=with_sqlite ./pkg/... ./server/camlistored ./cmd/... ./dev/...
|
||||
|
||||
oldpresubmit: fmt
|
||||
SKIP_DEP_TESTS=1 go test `pkg-config --libs sqlite3 1>/dev/null 2>/dev/null && echo "--tags=with_sqlite"` -short ./pkg/... ./server/camlistored/... ./server/appengine ./cmd/... ./dev/... && echo PASS
|
||||
|
||||
presubmit: fmt
|
||||
go run dev/devcam/*.go test -short
|
||||
|
||||
embeds:
|
||||
go install ./pkg/fileembed/genfileembed/ && genfileembed ./server/camlistored/ui && genfileembed ./pkg/server
|
||||
|
||||
UIDIR = server/camlistored/ui
|
||||
|
||||
NEWUIDIR = server/camlistored/newui
|
||||
|
||||
clean:
|
||||
rm -f $(NEWUIDIR)/all.js $(NEWUIDIR)/all.js.map
|
||||
|
||||
fmt:
|
||||
go fmt camlistore.org/cmd... camlistore.org/dev... camlistore.org/misc... camlistore.org/pkg... camlistore.org/server...
|
||||
23
vendor/github.com/camlistore/camlistore/README
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
Camlistore is your personal storage system for life.
|
||||
|
||||
It's a way to store, sync, share, model and back up content.
|
||||
|
||||
It stands for "Content-Addressable Multi-Layer Indexed Storage", for
|
||||
lack of a better name. For more, see:
|
||||
|
||||
http://camlistore.org/
|
||||
http://camlistore.org/docs/
|
||||
|
||||
Other useful files:
|
||||
|
||||
BUILDING how to compile it ("go run make.go")
|
||||
HACKING how to do development and contribute
|
||||
|
||||
Mailing lists:
|
||||
|
||||
http://camlistore.org/lists
|
||||
|
||||
Bugs and contributing:
|
||||
|
||||
https://github.com/camlistore/camlistore/issues
|
||||
http://camlistore.org/docs/contributing
|
||||
34
vendor/github.com/camlistore/camlistore/TESTS
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
Tests needed
|
||||
|
||||
-- integration test of reindexing + race detector
|
||||
|
||||
-- support for running race detector on all tests. when in race mode, also run
|
||||
integration test children in race mode.
|
||||
|
||||
-- test that server/camlistored still builds & starts even when sqlite isn't
|
||||
available (TODO: hide it from the test by running make.go in a child
|
||||
process with a faked-out PKG_CONFIG environment or something, to make
|
||||
cmd/go unable to find it even if it's installed)
|
||||
|
||||
-- search & corpus use of EnumeratePermanodesLastModified
|
||||
|
||||
-- pkg/client --- test FetchVia against a server returning compressed content.
|
||||
(fix in 3fa6d69405f036308931dd36e5070b2b19dbeadf without a new test)
|
||||
|
||||
-cmd/camput/
|
||||
-verify that stat caching works. verify that -filenodes does create the permanode even if the file was already uploaded (and cached) in a previous run.
|
||||
|
||||
-- blobserver/{remote,shard} have no tests. should be easier now that
|
||||
test.Fetcher is a full blobserver? see encrypt, replica, and cond's
|
||||
nascent tests for examples.
|
||||
|
||||
-- app engine integration tests (before we make a release, for sure,
|
||||
but probably in presubmit)
|
||||
|
||||
-- cross-compiling to freebsd and windows etc still works.
|
||||
|
||||
-- pkg/auth -- not enough tests. see regression at
|
||||
https://camlistore-review.googlesource.com/#/c/556/1
|
||||
|
||||
-- blobserver.WaitForBlob, and integration tests for the http handlers
|
||||
for long-polling on Enumerate and Stat
|
||||
253
vendor/github.com/camlistore/camlistore/TODO
generated
vendored
Normal file
@@ -0,0 +1,253 @@
|
||||
There are two TODO lists. This file (good for airplanes) and the online bug tracker:
|
||||
|
||||
https://github.com/camlistore/camlistore/issues
|
||||
|
||||
Offline list:
|
||||
|
||||
-- fix the presubmit's gofmt to be happy about emacs:
|
||||
|
||||
go fmt camlistore.org/cmd... camlistore.org/dev... camlistore.org/misc... camlistore.org/pkg... camlistore.org/server...
|
||||
stat pkg/blobserver/.#multistream_test.go: no such file or directory
|
||||
exit status 2
|
||||
make: *** [fmt] Error 1
|
||||
|
||||
|
||||
-- add HTTP handler for blobstreamer. stream a tar file? where to put
|
||||
continuation token? special file after each tar entry? special file
|
||||
at the end? HTTP Trailers? (but nobody supports them)
|
||||
|
||||
-- reindexing:
|
||||
* add streaming interface to localdisk? maybe, even though not ideal, but
|
||||
really: migrate my personal instance from localdisk to blobpacked +
|
||||
maybe diskpacked for loose blobs? start by migrating to blobpacked and
|
||||
measuring size of loose.
|
||||
* add blobserver.EnumerateAllUnsorted (which could use StreamBlobs
|
||||
if available, else use EnumerateAll, else maybe even use a new
|
||||
interface method that goes forever and can't resume at a point,
|
||||
but can be canceled, and localdisk could implement that at least)
|
||||
* add buffered sorted.KeyValue implementation: a memory one (of
|
||||
configurable max size) in front of a real disk one. add a Flush method
|
||||
to it. also Flush when memory gets big enough.
|
||||
In progress: pkg/sorted/buffer
|
||||
|
||||
-- stop using the "cond" blob router storage type in genconfig, as
|
||||
well as the /bs-and-index/ "replica" storage type, and just let the
|
||||
index register its own AddReceiveHook like the sync handler
|
||||
(pkg/server/sync.go). But whereas the sync handler only synchronously
|
||||
_enqueues_ the blob to replicate, the indexer should synchronously
|
||||
do the ReceiveBlob (ooo-reindex) on it too before returning.
|
||||
But the sync handler, despite technically only synchronously-enqueueing
|
||||
and being therefore async, is still very fast. It's likely the
|
||||
sync handler will therefore send a ReceiveBlob to the indexer
|
||||
at the ~same time the indexer is already indexing it. So the indexer
|
||||
should have some dup/merge suppression, and not do double work.
|
||||
singleflight should work. The loser should still consume the
|
||||
source io.Reader body and reply with the same error value.
|
||||
|
||||
-- ditch the importer.Interrupt type and pass along a context.Context
|
||||
instead, which has its Done channel for cancelation.
|
||||
|
||||
-- be able to put a search (expr or JSON) into camlistore as a blob,
|
||||
and search on it. and then name it with a permanode, and then
|
||||
use a expr search like "named:someset" which looks up someset's
|
||||
current camliContent, fetches it, and then expands into that blob's
|
||||
search.expr or search.Constraint.
|
||||
|
||||
-- S3-only mode doesn't work with a local disk index (kvfile) because
|
||||
there's no directory for us to put the kv in.
|
||||
|
||||
-- fault injection many more places with pkg/fault. maybe even in all
|
||||
handlers automatically somehow?
|
||||
|
||||
-- sync handler's shard validation doesn't retry on error.
|
||||
only reports the errors now.
|
||||
|
||||
-- export blobserver.checkHashReader and document it with
|
||||
the blob.Fetcher docs.
|
||||
|
||||
-- "filestogether" handler, putting related blobs (e.g. files)
|
||||
next to each other in bigger blobs / separate files, and recording
|
||||
offsets of small blobs into bigger ones
|
||||
|
||||
-- diskpacked doesn't seem to sync its index quickly enough.
|
||||
A new blob receieved + process exit + read in a new process
|
||||
doesn't find that blob. kv bug? Seems to need an explicit Close.
|
||||
This feels broken. Add tests & debug.
|
||||
|
||||
-- websocket upload protocol. different write & read on same socket,
|
||||
as opposed to HTTP, to have multiple chunks in flight.
|
||||
|
||||
-- extension to blobserver upload protocol to minimize fsyncs: maybe a
|
||||
client can say "no rush" on a bunch of data blobs first (which
|
||||
still don't get acked back over websocket until they've been
|
||||
fsynced), and then when the client uploads the schema/vivivy blob,
|
||||
that websocket message won't have the "no rush" flag, calling the
|
||||
optional blobserver.Storage method to fsync (in the case of
|
||||
diskpacked/localdisk) and getting all the "uploaded" messages back
|
||||
for the data chunks that were written-but-not-synced.
|
||||
|
||||
-- measure FUSE operations, latency, round-trips, performance.
|
||||
see next item:
|
||||
|
||||
-- ... we probaby need a "describe all chunks in file" HTTP handler.
|
||||
then FUSE (when it sees sequential access) can say "what's the
|
||||
list of all chunks in this file?" and then fetch them all at once.
|
||||
see next item:
|
||||
|
||||
-- ... HTTP handler to get multiple blobs at once. multi-download
|
||||
in multipart/mime body. we have this for stat and upload, but
|
||||
not download.
|
||||
|
||||
-- ... if we do blob fetching over websocket too, then we can support
|
||||
cancellation of blob requests. Then we can combine the previous
|
||||
two items: FUSE client can ask the server, over websockets, for a
|
||||
list of all chunks, and to also start streaming them all. assume a
|
||||
high-latency (but acceptable bandwidth) link. the chunks are
|
||||
already in flight, but some might be redundant. once the client figures
|
||||
out some might be redundant, it can issue "stop send" messages over
|
||||
that websocket connection to prevent dups. this should work on
|
||||
both "files" and "bytes" types.
|
||||
|
||||
-- cacher: configurable policy on max cache size. clean oldest
|
||||
things (consider mtime+atime) to get back under max cache size.
|
||||
maybe prefer keeping small things (metadata blobs) too,
|
||||
and only delete large data chunks.
|
||||
|
||||
-- UI: video, at least thumbnailing (use external program,
|
||||
like VLC or whatever nautilus uses?)
|
||||
|
||||
-- rename server.ImageHandler to ThumbnailRequest or something? It's
|
||||
not really a Handler in the normal sense. It's not built once and
|
||||
called repeatedly; it's built for every ServeHTTP request.
|
||||
|
||||
-- unexport more stuff from pkg/server. Cache, etc.
|
||||
|
||||
-- look into garbage from openpgp signing
|
||||
|
||||
-- make leveldb memdb's iterator struct only 8 bytes, pointing to a recycled
|
||||
object, and just nil out that pointer at EOF.
|
||||
|
||||
-- bring in the google glog package to third_party and use it in
|
||||
places that want selective logging (e.g. pkg/index/receive.go)
|
||||
|
||||
-- (Mostly done) verify all ReceiveBlob calls and see which should be
|
||||
blobserver.Receive instead, or ReceiveNoHash. git grep -E
|
||||
"\.ReceiveBlob\(" And maybe ReceiveNoHash should go away and be
|
||||
replaced with a "ReceiveString" method which combines the
|
||||
blobref-from-string and ReceiveNoHash at once.
|
||||
|
||||
-- union storage target. sharder can be thought of a specialization
|
||||
of union. sharder already unions, but has a hard-coded policy
|
||||
of where to put new blobs. union could a library (used by sharder)
|
||||
with a pluggable policy on that.
|
||||
|
||||
-- support for running cammount under camlistored. especially for OS X,
|
||||
where the lifetime of the background daemon will be the same as the
|
||||
user's login session.
|
||||
|
||||
-- website: remove the "Installation" heading for /cmd/*, since
|
||||
they're misleading and people should use "go run make.go" in the
|
||||
general case.
|
||||
|
||||
-- website: add godoc for /server/camlistored (also without a "go get"
|
||||
line)
|
||||
|
||||
-- tests for all cmd/* stuff, perhaps as part of some integration
|
||||
tests.
|
||||
|
||||
-- move most of camput into a library, not a package main.
|
||||
|
||||
-- server cron support: full syncs, camput file backups, integrity
|
||||
checks.
|
||||
|
||||
-- status in top right of UI: sync, crons. (in-progress, un-acked
|
||||
problems)
|
||||
|
||||
-- finish metadata compaction on the encryption blobserver.Storage wrapper.
|
||||
|
||||
-- get security review on encryption wrapper. (agl?)
|
||||
|
||||
-- peer-to-peer server and blobserver target to store encrypted blobs
|
||||
on stranger's hardrives. server will be open source so groups of
|
||||
friends/family can run their own for small circles, or some company
|
||||
could run a huge instance. spray encrypted backup chunks across
|
||||
friends' machines, and have central server(s) present challenges to
|
||||
the replicas to have them verify what they have and how big, and
|
||||
also occasionally say what the SHA-1("challenge" + blob-data) is.
|
||||
|
||||
-- sharing: make camget work with permanode sets too, not just
|
||||
"directory" and "file" things.
|
||||
|
||||
-- sharing: when hitting e.g. http://myserver/share/sha1-xxxxx, if
|
||||
a web browser and not a smart client (Accept header? User-Agent?)
|
||||
then redirect or render a cutesy gallery or file browser instead,
|
||||
still with machine-readable data for slurping.
|
||||
|
||||
-- rethink the directory schema so it can a) represent directories
|
||||
with millions of files (without making a >1MB or >16MB schema blob),
|
||||
probably forming a tree, similar to files. but rather than rolling checksum,
|
||||
just split lexically when nodes get too big.
|
||||
|
||||
-- delete mostly-obsolete camsigd. see big TODO in camsigd.go.
|
||||
|
||||
-- we used to be able live-edit js/css files in server/camlistored/ui when
|
||||
running under the App Engine dev_appserver.py. That's now broken with my
|
||||
latest efforts to revive it. The place to start looking is:
|
||||
server/camlistored/ui/fileembed_appengine.go
|
||||
|
||||
-- should a "share" claim be not a claim but its own permanode, so it
|
||||
can be rescinded? right now you can't really unshare a "haveref"
|
||||
claim. or rather, TODO: verify we support "delete" claims to
|
||||
delete any claim, and verify the share system and indexer all
|
||||
support it. I think the indexer might, but not the share system.
|
||||
Also TODO: "camput delete" or "rescind" subcommand.
|
||||
Also TODO: document share claims in doc/schema/ and on website.
|
||||
|
||||
-- make the -transitive flag for "camput share -transitive" be a tri-state:
|
||||
unset, true, false, and unset should then mean default to true for "file"
|
||||
and "directory" schema blobs, and "false" for other things.
|
||||
|
||||
-- index: static directory recursive sizes: search: ask to see biggest directories?
|
||||
|
||||
-- index: index dates in filenames ("yyyy-mm-dd-Foo-Trip", "yyyy-mm blah", etc).
|
||||
|
||||
-- get webdav server working again, for mounting on Windows. This worked before Go 1
|
||||
but bitrot when we moved pkg/fs to use the rsc/fuse.
|
||||
|
||||
-- work on runsit more, so I can start using this more often. runsit should
|
||||
be able to reload itself, and also watch for binaries changing and restart
|
||||
when binaries change. (or symlinks to binaries)
|
||||
|
||||
-- BUG: osutil paths.go on OS X: should use Library everywhere instead of mix of
|
||||
Library and ~/.camlistore?
|
||||
|
||||
OLD:
|
||||
|
||||
-- add CROS support? Access-Control-Allow-Origin: * + w/ OPTIONS
|
||||
http://hacks.mozilla.org/2009/07/cross-site-xmlhttprequest-with-cors/
|
||||
|
||||
-- brackup integration, perhaps sans GPG? (requires Perl client?)
|
||||
|
||||
-- blobserver: clean up channel-closing consistency in blobserver interface
|
||||
(most close, one doesn't. all should probably close)
|
||||
|
||||
Android:
|
||||
|
||||
[ ] Fix wake locks in UploadThread. need to hold CPU + WiFi whenever
|
||||
something's enqueued at all and we're running. Move out of the Thread
|
||||
that's uploading itself.
|
||||
[ ] GPG signing of blobs (brad)
|
||||
http://code.google.com/p/android-privacy-guard/
|
||||
http://www.thialfihar.org/projects/apg/
|
||||
(supports signing in code, but not an Intent?)
|
||||
http://code.google.com/p/android-privacy-guard/wiki/UsingApgForDevelopment
|
||||
... mailed the author.
|
||||
|
||||
Client libraries:
|
||||
|
||||
[X] Go
|
||||
[X] JavaScript
|
||||
[/] Python (Brett); but see https://github.com/tsileo/camlipy
|
||||
[ ] Perl
|
||||
[ ] Ruby
|
||||
[ ] PHP
|
||||
97
vendor/github.com/camlistore/camlistore/app/hello/main.go
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
Copyright 2014 The Camlistore Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// The hello application serves as an example on how to make stand-alone
|
||||
// server applications, interacting with a Camlistore server.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"camlistore.org/pkg/app"
|
||||
"camlistore.org/pkg/buildinfo"
|
||||
"camlistore.org/pkg/webserver"
|
||||
)
|
||||
|
||||
var (
|
||||
flagVersion = flag.Bool("version", false, "show version")
|
||||
)
|
||||
|
||||
// config is used to unmarshal the application configuration JSON
|
||||
// that we get from Camlistore when we request it at $CAMLI_APP_CONFIG_URL.
|
||||
type config struct {
|
||||
Word string `json:"word,omitempty"` // Argument printed after "Hello " in the helloHandler response.
|
||||
}
|
||||
|
||||
func appConfig() *config {
|
||||
configURL := os.Getenv("CAMLI_APP_CONFIG_URL")
|
||||
if configURL == "" {
|
||||
log.Fatalf("Hello application needs a CAMLI_APP_CONFIG_URL env var")
|
||||
}
|
||||
cl, err := app.Client()
|
||||
if err != nil {
|
||||
log.Fatalf("could not get a client to fetch extra config: %v", err)
|
||||
}
|
||||
conf := &config{}
|
||||
if err := cl.GetJSON(configURL, conf); err != nil {
|
||||
log.Fatalf("could not get app config at %v: %v", configURL, err)
|
||||
}
|
||||
return conf
|
||||
}
|
||||
|
||||
type helloHandler struct {
|
||||
who string // who to say hello to.
|
||||
}
|
||||
|
||||
func (h *helloHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
rw.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||
rw.WriteHeader(200)
|
||||
fmt.Fprintf(rw, "Hello %s\n", h.who)
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
if *flagVersion {
|
||||
fmt.Fprintf(os.Stderr, "hello version: %s\nGo version: %s (%s/%s)\n",
|
||||
buildinfo.Version(), runtime.Version(), runtime.GOOS, runtime.GOARCH)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Starting hello version %s; Go %s (%s/%s)", buildinfo.Version(), runtime.Version(),
|
||||
runtime.GOOS, runtime.GOARCH)
|
||||
|
||||
listenAddr, err := app.ListenAddress()
|
||||
if err != nil {
|
||||
log.Fatalf("Listen address: %v", err)
|
||||
}
|
||||
conf := appConfig()
|
||||
ws := webserver.New()
|
||||
ws.Handle("/", &helloHandler{who: conf.Word})
|
||||
// TODO(mpl): handle status requests too. Camlistore will send an auth
|
||||
// token in the extra config that should be used as the "password" for
|
||||
// subsequent status requests.
|
||||
if err := ws.Listen(listenAddr); err != nil {
|
||||
log.Fatalf("Listen: %v", err)
|
||||
}
|
||||
|
||||
ws.Serve()
|
||||
}
|
||||
32
vendor/github.com/camlistore/camlistore/app/publisher/fileembed.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
Copyright 2014 The Camlistore Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
The publisher application serves and renders items published by Camlistore.
|
||||
That is, items that are children, through a (direct or not) camliPath relation,
|
||||
of a camliRoot node (a permanode with a camliRoot attribute set).
|
||||
|
||||
#fileembed pattern .+\.(js|css|html|png|svg)$
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"camlistore.org/pkg/fileembed"
|
||||
)
|
||||
|
||||
// TODO(mpl): appengine case
|
||||
|
||||
var Files = &fileembed.Files{}
|
||||
53
vendor/github.com/camlistore/camlistore/app/publisher/gallery.html
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
{{if $header := call .Header}}
|
||||
<head>
|
||||
<title>{{$header.Title}}</title>
|
||||
{{range $css := $header.CSSFiles}}
|
||||
<link rel='stylesheet' type='text/css' href='{{$css}}'>
|
||||
{{end}}
|
||||
<script>
|
||||
var camliViewIsOwner = {{$header.ViewerIsOwner}};
|
||||
var camliPagePermanode = {{$header.Subject}};
|
||||
var camliPageMeta =
|
||||
{{$header.Meta}};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{$header.Title}}</h1>
|
||||
{{if $file := call .File}}
|
||||
<div>File: {{$file.FileName}}, {{$file.Size}} bytes, type {{$file.MIMEType}}</div>
|
||||
{{if $file.IsImage}}
|
||||
<a href='{{$file.DownloadURL}}'><img src='{{$file.ThumbnailURL}}'></a>
|
||||
{{end}}
|
||||
<div id='{{$file.DomID}}' class='camlifile'>[<a href='{{$file.DownloadURL}}'>download</a>]</div>
|
||||
{{if $nav := call $file.Nav}}
|
||||
<div class='camlifile'>
|
||||
{{if $prev := $nav.PrevPath}}[<a href='{{$prev}}'>prev</a>] {{end}}
|
||||
{{if $up := $nav.ParentPath}}[<a href='{{$up}}'>up</a>] {{end}}
|
||||
{{if $next := $nav.NextPath}}[<a href='{{$next}}'>next</a>] {{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{else}}
|
||||
{{if $membersData := call .Members}}
|
||||
<div><a href='{{$membersData.SubjectPath}}/=z/{{html $membersData.ZipName | urlquery}}'>{{html $membersData.ZipName}}</a></div>
|
||||
<!-- TODO(mpl): something's messed up with the hidden edit title position, it should appear under the image. -->
|
||||
<ul id='members'>
|
||||
{{range $member := $membersData.Members}}
|
||||
<li id='{{call $membersData.DomID $member}}'>
|
||||
<a href='{{call $membersData.Path $member}}'>
|
||||
{{$fileInfo := call $membersData.FileInfo $member}}
|
||||
<img src='{{if $fileInfo}}{{$fileInfo.FileThumbnailURL}}{{end}}'>
|
||||
<span>{{call $membersData.Title $member}}</span></a>
|
||||
{{call $membersData.Description $member}}
|
||||
<div id='{{if $fileInfo}}{{$fileInfo.FileDomID}}{{end}}' class='camlifile'>
|
||||
<a href='{{if $fileInfo}}{{$fileInfo.FilePath}}{{end}}'>file</a>
|
||||
</div>
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
</body>
|
||||
</html>
|
||||
1008
vendor/github.com/camlistore/camlistore/app/publisher/main.go
generated
vendored
Normal file
112
vendor/github.com/camlistore/camlistore/app/publisher/pics.css
generated
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
Copyright 2012 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/* Something arbitrary for testing. */
|
||||
body {
|
||||
font: 13px/1.3 normal Verdana, Geneva, sans-serif;
|
||||
background: #000;
|
||||
color: #aaa;
|
||||
margin: 0;
|
||||
padding: 30px;
|
||||
}
|
||||
a {
|
||||
color: #aaa;
|
||||
}
|
||||
a:hover {
|
||||
color: #bbb;
|
||||
}
|
||||
h1 {
|
||||
border: 3px dashed #aaa;
|
||||
padding: 1em;
|
||||
}
|
||||
ul {
|
||||
list-style: none;
|
||||
background: #262626;
|
||||
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
li {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
padding: 1em;
|
||||
margin: 1em;
|
||||
text-align: right;
|
||||
}
|
||||
li:hover {
|
||||
background: #000;
|
||||
border-radius: 10px;
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
li:hover img {
|
||||
xmax-height: none;
|
||||
xmax-width: none;
|
||||
}
|
||||
li a {
|
||||
font-weight: bold;
|
||||
}
|
||||
li img {
|
||||
border: 0;
|
||||
border-radius: 6px;
|
||||
display: block;
|
||||
max-height: 200px;
|
||||
max-width: 200px;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
li .camlifile {
|
||||
display: none;
|
||||
}
|
||||
li a span {
|
||||
padding: 2px;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
li input {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
a.title-edit,
|
||||
a.title-edit:hover {
|
||||
font-size: 70%;
|
||||
color: #f00;
|
||||
margin-right: .5em;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
a.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
a.visible {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
input.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
input.visible {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
span.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
span.visible {
|
||||
display: inline-block;
|
||||
}
|
||||
234
vendor/github.com/camlistore/camlistore/app/publisher/publish_test.go
generated
vendored
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
Copyright 2014 The Camlistore Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
camliClient "camlistore.org/pkg/client"
|
||||
"camlistore.org/pkg/httputil"
|
||||
"camlistore.org/pkg/index"
|
||||
"camlistore.org/pkg/index/indextest"
|
||||
"camlistore.org/pkg/search"
|
||||
)
|
||||
|
||||
type publishURLTest struct {
|
||||
path string // input
|
||||
subject, subres string // expected
|
||||
}
|
||||
|
||||
var publishURLTests []publishURLTest
|
||||
|
||||
func setupContent(rootName string) *indextest.IndexDeps {
|
||||
idx := index.NewMemoryIndex()
|
||||
idxd := indextest.NewIndexDeps(idx)
|
||||
|
||||
picNode := idxd.NewPlannedPermanode("picpn-1234") // sha1-f5e90fcc50a79caa8b22a4aa63ba92e436cab9ec
|
||||
galRef := idxd.NewPlannedPermanode("gal-1234") // sha1-2bdf2053922c3dfa70b01a4827168fce1c1df691
|
||||
rootRef := idxd.NewPlannedPermanode("root-abcd") // sha1-dbb3e5f28c7e01536d43ce194f3dd7b921b8460d
|
||||
camp0 := idxd.NewPlannedPermanode("picpn-9876543210") // sha1-2d473e07ca760231dd82edeef4019d5b7d0ccb42
|
||||
camp1 := idxd.NewPlannedPermanode("picpn-9876543211") // sha1-961b700536d5151fc1f3920955cc92767572a064
|
||||
camp0f, _ := idxd.UploadFile("picfile-f00ff00f00a5.jpg", "picfile-f00ff00f00a5", time.Time{}) // sha1-01dbcb193fc789033fb2d08ed22abe7105b48640
|
||||
camp1f, _ := idxd.UploadFile("picfile-f00ff00f00b6.jpg", "picfile-f00ff00f00b6", time.Time{}) // sha1-1213ec17a42cc51bdeb95ff91ac1b5fc5157740f
|
||||
|
||||
idxd.SetAttribute(rootRef, "camliRoot", rootName)
|
||||
idxd.SetAttribute(rootRef, "camliPath:singlepic", picNode.String())
|
||||
idxd.SetAttribute(picNode, "title", "picnode without a pic?")
|
||||
idxd.SetAttribute(rootRef, "camliPath:camping", galRef.String())
|
||||
idxd.AddAttribute(galRef, "camliMember", camp0.String())
|
||||
idxd.AddAttribute(galRef, "camliMember", camp1.String())
|
||||
idxd.SetAttribute(camp0, "camliContent", camp0f.String())
|
||||
idxd.SetAttribute(camp1, "camliContent", camp1f.String())
|
||||
|
||||
publishURLTests = []publishURLTest{
|
||||
// URL to a single picture permanode (returning its HTML wrapper page)
|
||||
{
|
||||
path: "/pics/singlepic",
|
||||
subject: picNode.String(),
|
||||
},
|
||||
|
||||
// URL to a gallery permanode (returning its HTML wrapper page)
|
||||
{
|
||||
path: "/pics/camping",
|
||||
subject: galRef.String(),
|
||||
},
|
||||
|
||||
// URL to a picture permanode within a gallery (following one hop, returning HTML)
|
||||
{
|
||||
path: "/pics/camping/-/h2d473e07ca",
|
||||
subject: camp0.String(),
|
||||
},
|
||||
|
||||
// URL to a gallery -> picture permanode -> its file
|
||||
// (following two hops, returning HTML)
|
||||
{
|
||||
path: "/pics/camping/-/h2d473e07ca/h01dbcb193f",
|
||||
subject: camp0f.String(),
|
||||
},
|
||||
|
||||
// URL to a gallery -> picture permanode -> its file
|
||||
// (following two hops, returning the file download)
|
||||
{
|
||||
path: "/pics/camping/-/h2d473e07ca/h01dbcb193f/=f/marshmallow.jpg",
|
||||
subject: camp0f.String(),
|
||||
subres: "/=f/marshmallow.jpg",
|
||||
},
|
||||
|
||||
// URL to a gallery -> picture permanode -> its file
|
||||
// (following two hops, returning the file, scaled as an image)
|
||||
{
|
||||
path: "/pics/camping/-/h961b700536/h1213ec17a4/=i/marshmallow.jpg?mw=200&mh=200",
|
||||
subject: camp1f.String(),
|
||||
subres: "/=i/marshmallow.jpg",
|
||||
},
|
||||
|
||||
// Path to a static file in the root.
|
||||
// TODO: ditch these and use content-addressable javascript + css, having
|
||||
// the server digest them on start, or rather part of fileembed. This is
|
||||
// a short-term hack to unblock Lindsey.
|
||||
{
|
||||
path: "/pics/=s/pics.js",
|
||||
subject: "",
|
||||
subres: "/=s/pics.js",
|
||||
},
|
||||
}
|
||||
|
||||
return idxd
|
||||
}
|
||||
|
||||
type fakeClient struct {
|
||||
*camliClient.Client // for blob.Fetcher
|
||||
sh *search.Handler
|
||||
}
|
||||
|
||||
func (fc *fakeClient) Search(req *search.SearchQuery) (*search.SearchResult, error) {
|
||||
return fc.sh.Query(req)
|
||||
}
|
||||
|
||||
func (fc *fakeClient) Describe(req *search.DescribeRequest) (*search.DescribeResponse, error) {
|
||||
return fc.sh.Describe(req)
|
||||
}
|
||||
|
||||
func (fc *fakeClient) GetJSON(url string, data interface{}) error {
|
||||
// no need to implement
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fc *fakeClient) Post(url string, bodyType string, body io.Reader) error {
|
||||
// no need to implement
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestPublishURLs(t *testing.T) {
|
||||
rootName := "foo"
|
||||
idxd := setupContent(rootName)
|
||||
sh := search.NewHandler(idxd.Index, idxd.SignerBlobRef)
|
||||
corpus, err := idxd.Index.KeepInMemory()
|
||||
if err != nil {
|
||||
t.Fatalf("error slurping index to memory: %v", err)
|
||||
}
|
||||
sh.SetCorpus(corpus)
|
||||
cl := camliClient.New("http://whatever.fake")
|
||||
fcl := &fakeClient{cl, sh}
|
||||
ph := &publishHandler{
|
||||
rootName: rootName,
|
||||
cl: fcl,
|
||||
}
|
||||
if err := ph.initRootNode(); err != nil {
|
||||
t.Fatalf("initRootNode: %v", err)
|
||||
}
|
||||
|
||||
for ti, tt := range publishURLTests {
|
||||
rw := httptest.NewRecorder()
|
||||
if !strings.HasPrefix(tt.path, "/pics/") {
|
||||
panic("expected /pics/ prefix on " + tt.path)
|
||||
}
|
||||
req, _ := http.NewRequest("GET", "http://foo.com"+tt.path, nil)
|
||||
|
||||
pfxh := &httputil.PrefixHandler{
|
||||
Prefix: "/pics/",
|
||||
Handler: http.HandlerFunc(func(_ http.ResponseWriter, req *http.Request) {
|
||||
pr, err := ph.NewRequest(rw, req)
|
||||
if err != nil {
|
||||
t.Fatalf("test #%d, NewRequest: %v", ti, err)
|
||||
}
|
||||
|
||||
err = pr.findSubject()
|
||||
if tt.subject != "" {
|
||||
if err != nil {
|
||||
t.Errorf("test #%d, findSubject: %v", ti, err)
|
||||
return
|
||||
}
|
||||
if pr.subject.String() != tt.subject {
|
||||
t.Errorf("test #%d, got subject %q, want %q", ti, pr.subject, tt.subject)
|
||||
}
|
||||
}
|
||||
if pr.subres != tt.subres {
|
||||
t.Errorf("test #%d, got subres %q, want %q", ti, pr.subres, tt.subres)
|
||||
}
|
||||
}),
|
||||
}
|
||||
pfxh.ServeHTTP(rw, req)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublishMembers(t *testing.T) {
|
||||
rootName := "foo"
|
||||
idxd := setupContent(rootName)
|
||||
|
||||
sh := search.NewHandler(idxd.Index, idxd.SignerBlobRef)
|
||||
corpus, err := idxd.Index.KeepInMemory()
|
||||
if err != nil {
|
||||
t.Fatalf("error slurping index to memory: %v", err)
|
||||
}
|
||||
sh.SetCorpus(corpus)
|
||||
cl := camliClient.New("http://whatever.fake")
|
||||
fcl := &fakeClient{cl, sh}
|
||||
ph := &publishHandler{
|
||||
rootName: rootName,
|
||||
cl: fcl,
|
||||
}
|
||||
|
||||
rw := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "http://foo.com/pics", nil)
|
||||
|
||||
pfxh := &httputil.PrefixHandler{
|
||||
Prefix: "/pics/",
|
||||
Handler: http.HandlerFunc(func(_ http.ResponseWriter, req *http.Request) {
|
||||
pr, err := ph.NewRequest(rw, req)
|
||||
if err != nil {
|
||||
t.Fatalf("NewRequest: %v", err)
|
||||
}
|
||||
|
||||
res, err := pr.ph.deepDescribe(pr.subject)
|
||||
if err != nil {
|
||||
t.Fatalf("deepDescribe: %v", err)
|
||||
}
|
||||
|
||||
members, err := pr.subjectMembers(res.Meta)
|
||||
if len(members.Members) != 2 {
|
||||
t.Errorf("Expected two members in publish root (one camlipath, one camlimember), got %d", len(members.Members))
|
||||
}
|
||||
}),
|
||||
}
|
||||
pfxh.ServeHTTP(rw, req)
|
||||
}
|
||||
308
vendor/github.com/camlistore/camlistore/app/publisher/zip.go
generated
vendored
Normal file
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
Copyright 2013 The Camlistore Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"mime"
|
||||
"net/http"
|
||||
"path"
|
||||
"sort"
|
||||
|
||||
"camlistore.org/pkg/blob"
|
||||
"camlistore.org/pkg/httputil"
|
||||
"camlistore.org/pkg/schema"
|
||||
"camlistore.org/pkg/search"
|
||||
"camlistore.org/pkg/types/camtypes"
|
||||
)
|
||||
|
||||
type zipHandler struct {
|
||||
fetcher blob.Fetcher
|
||||
cl client // Used for search and describe requests.
|
||||
// root is the "parent" permanode of everything to zip.
|
||||
// Either a directory permanode, or a permanode with members.
|
||||
root blob.Ref
|
||||
// Optional name to use in the response header
|
||||
filename string
|
||||
}
|
||||
|
||||
// blobFile contains all the information we need about
|
||||
// a file blob to add the corresponding file to a zip.
|
||||
type blobFile struct {
|
||||
blobRef blob.Ref
|
||||
// path is the full path of the file from the root of the zip.
|
||||
// slashes are always forward slashes, per the zip spec.
|
||||
path string
|
||||
}
|
||||
|
||||
type sortedFiles []*blobFile
|
||||
|
||||
func (s sortedFiles) Less(i, j int) bool { return s[i].path < s[j].path }
|
||||
func (s sortedFiles) Len() int { return len(s) }
|
||||
func (s sortedFiles) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
func (zh *zipHandler) describeMembers(br blob.Ref) (*search.DescribeResponse, error) {
|
||||
res, err := zh.cl.Query(&search.SearchQuery{
|
||||
Constraint: &search.Constraint{
|
||||
BlobRefPrefix: br.String(),
|
||||
CamliType: "permanode",
|
||||
},
|
||||
Describe: &search.DescribeRequest{
|
||||
Depth: 1,
|
||||
Rules: []*search.DescribeRule{
|
||||
{
|
||||
Attrs: []string{"camliContent", "camliContentImage", "camliMember"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Limit: -1,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not describe %v: %v", br, err)
|
||||
}
|
||||
if res == nil || res.Describe == nil {
|
||||
return nil, fmt.Errorf("no describe result for %v", br)
|
||||
}
|
||||
return res.Describe, nil
|
||||
}
|
||||
|
||||
// blobList returns the list of file blobs "under" dirBlob.
|
||||
// It traverses permanode directories and permanode with members (collections).
|
||||
func (zh *zipHandler) blobList(dirPath string, dirBlob blob.Ref) ([]*blobFile, error) {
|
||||
// dr := zh.search.NewDescribeRequest()
|
||||
// dr.Describe(dirBlob, 3)
|
||||
// res, err := dr.Result()
|
||||
// if err != nil {
|
||||
// return nil, fmt.Errorf("Could not describe %v: %v", dirBlob, err)
|
||||
// }
|
||||
res, err := zh.describeMembers(dirBlob)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
described := res.Meta[dirBlob.String()]
|
||||
members := described.Members()
|
||||
dirBlobPath, _, isDir := described.PermanodeDir()
|
||||
if len(members) == 0 && !isDir {
|
||||
return nil, nil
|
||||
}
|
||||
var list []*blobFile
|
||||
if isDir {
|
||||
dirRoot := dirBlobPath[1]
|
||||
children, err := zh.blobsFromDir("/", dirRoot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not get list of blobs from %v: %v", dirRoot, err)
|
||||
}
|
||||
list = append(list, children...)
|
||||
return list, nil
|
||||
}
|
||||
for _, member := range members {
|
||||
if fileBlobPath, fileInfo, ok := getFileInfo(member.BlobRef, res.Meta); ok {
|
||||
// file
|
||||
list = append(list,
|
||||
&blobFile{fileBlobPath[1], path.Join(dirPath, fileInfo.FileName)})
|
||||
continue
|
||||
}
|
||||
if dirBlobPath, dirInfo, ok := getDirInfo(member.BlobRef, res.Meta); ok {
|
||||
// directory
|
||||
newZipRoot := dirBlobPath[1]
|
||||
children, err := zh.blobsFromDir(
|
||||
path.Join(dirPath, dirInfo.FileName), newZipRoot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not get list of blobs from %v: %v", newZipRoot, err)
|
||||
}
|
||||
list = append(list, children...)
|
||||
// TODO(mpl): we assume a directory permanode does not also have members.
|
||||
// I know there is nothing preventing it, but does it make any sense?
|
||||
continue
|
||||
}
|
||||
// it might have members, so recurse
|
||||
// If it does have members, we must consider it as a pseudo dir,
|
||||
// so we can build a fullpath for each of its members.
|
||||
// As a dir name, we're using its title if it has one, its (shortened)
|
||||
// blobref otherwise.
|
||||
pseudoDirName := member.Title()
|
||||
if pseudoDirName == "" {
|
||||
pseudoDirName = member.BlobRef.DigestPrefix(10)
|
||||
}
|
||||
fullpath := path.Join(dirPath, pseudoDirName)
|
||||
moreMembers, err := zh.blobList(fullpath, member.BlobRef)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not get list of blobs from %v: %v", member.BlobRef, err)
|
||||
}
|
||||
list = append(list, moreMembers...)
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// blobsFromDir returns the list of file blobs in directory dirBlob.
|
||||
// It only traverses permanode directories.
|
||||
func (zh *zipHandler) blobsFromDir(dirPath string, dirBlob blob.Ref) ([]*blobFile, error) {
|
||||
var list []*blobFile
|
||||
dr, err := schema.NewDirReader(zh.fetcher, dirBlob)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not read dir blob %v: %v", dirBlob, err)
|
||||
}
|
||||
ent, err := dr.Readdir(-1)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not read dir entries: %v", err)
|
||||
}
|
||||
for _, v := range ent {
|
||||
fullpath := path.Join(dirPath, v.FileName())
|
||||
switch v.CamliType() {
|
||||
case "file":
|
||||
list = append(list, &blobFile{v.BlobRef(), fullpath})
|
||||
case "directory":
|
||||
children, err := zh.blobsFromDir(fullpath, v.BlobRef())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not get list of blobs from %v: %v", v.BlobRef(), err)
|
||||
}
|
||||
list = append(list, children...)
|
||||
}
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// renameDuplicates goes through bf to check for duplicate filepaths.
|
||||
// It renames duplicate filepaths and returns a new slice, sorted by
|
||||
// file path.
|
||||
func renameDuplicates(bf []*blobFile) sortedFiles {
|
||||
noDup := make(map[string]blob.Ref)
|
||||
// use a map to detect duplicates and rename them
|
||||
for _, file := range bf {
|
||||
if _, ok := noDup[file.path]; ok {
|
||||
// path already exists, so rename
|
||||
suffix := 0
|
||||
var newname string
|
||||
for {
|
||||
suffix++
|
||||
ext := path.Ext(file.path)
|
||||
newname = fmt.Sprintf("%s(%d)%s",
|
||||
file.path[:len(file.path)-len(ext)], suffix, ext)
|
||||
if _, ok := noDup[newname]; !ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
noDup[newname] = file.blobRef
|
||||
} else {
|
||||
noDup[file.path] = file.blobRef
|
||||
}
|
||||
}
|
||||
|
||||
// reinsert in a slice and sort it
|
||||
var sorted sortedFiles
|
||||
for p, b := range noDup {
|
||||
sorted = append(sorted, &blobFile{path: p, blobRef: b})
|
||||
}
|
||||
sort.Sort(sorted)
|
||||
return sorted
|
||||
}
|
||||
|
||||
// ServeHTTP streams a zip archive of all the files "under"
|
||||
// zh.root. That is, all the files pointed by file permanodes,
|
||||
// which are directly members of zh.root or recursively down
|
||||
// directory permanodes and permanodes members.
|
||||
// To build the fullpath of a file in a collection, it uses
|
||||
// the collection title if present, its blobRef otherwise, as
|
||||
// a directory name.
|
||||
func (zh *zipHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
// TODO: use http.ServeContent, so Range requests work and downloads can be resumed.
|
||||
// Will require calculating the zip length once first (ideally as cheaply as possible,
|
||||
// with dummy counting writer and dummy all-zero-byte-files of a fixed size),
|
||||
// and then making a dummy ReadSeeker for ServeContent that can seek to the end,
|
||||
// and then seek back to the beginning, but then seeks forward make it remember
|
||||
// to skip that many bytes from the archive/zip writer when answering Reads.
|
||||
if !httputil.IsGet(req) {
|
||||
http.Error(rw, "Invalid method", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
bf, err := zh.blobList("/", zh.root)
|
||||
if err != nil {
|
||||
log.Printf("Could not serve zip for %v: %v", zh.root, err)
|
||||
http.Error(rw, "Server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
blobFiles := renameDuplicates(bf)
|
||||
|
||||
// TODO(mpl): streaming directly won't work on appengine if the size goes
|
||||
// over 32 MB. Deal with that.
|
||||
h := rw.Header()
|
||||
h.Set("Content-Type", "application/zip")
|
||||
filename := zh.filename
|
||||
if filename == "" {
|
||||
filename = "download.zip"
|
||||
}
|
||||
h.Set("Content-Disposition", mime.FormatMediaType("attachment", map[string]string{"filename": filename}))
|
||||
zw := zip.NewWriter(rw)
|
||||
etag := sha1.New()
|
||||
for _, file := range blobFiles {
|
||||
etag.Write([]byte(file.blobRef.String()))
|
||||
}
|
||||
h.Set("Etag", fmt.Sprintf(`"%x"`, etag.Sum(nil)))
|
||||
|
||||
for _, file := range blobFiles {
|
||||
fr, err := schema.NewFileReader(zh.fetcher, file.blobRef)
|
||||
if err != nil {
|
||||
log.Printf("Can not add %v in zip, not a file: %v", file.blobRef, err)
|
||||
http.Error(rw, "Server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
f, err := zw.CreateHeader(
|
||||
&zip.FileHeader{
|
||||
Name: file.path,
|
||||
Method: zip.Store,
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("Could not create %q in zip: %v", file.path, err)
|
||||
http.Error(rw, "Server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
_, err = io.Copy(f, fr)
|
||||
fr.Close()
|
||||
if err != nil {
|
||||
log.Printf("Could not zip %q: %v", file.path, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
err = zw.Close()
|
||||
if err != nil {
|
||||
log.Printf("Could not close zipwriter: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(mpl): refactor with getFileInfo
|
||||
func getDirInfo(item blob.Ref, peers map[string]*search.DescribedBlob) (path []blob.Ref, di *camtypes.FileInfo, ok bool) {
|
||||
described := peers[item.String()]
|
||||
if described == nil ||
|
||||
described.Permanode == nil ||
|
||||
described.Permanode.Attr == nil {
|
||||
return
|
||||
}
|
||||
contentRef := described.Permanode.Attr.Get("camliContent")
|
||||
if contentRef == "" {
|
||||
return
|
||||
}
|
||||
if cdes := peers[contentRef]; cdes != nil && cdes.Dir != nil {
|
||||
return []blob.Ref{described.BlobRef, cdes.BlobRef}, cdes.Dir, true
|
||||
}
|
||||
return
|
||||
}
|
||||
9
vendor/github.com/camlistore/camlistore/clients/android/.classpath
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
|
||||
<classpathentry kind="output" path="bin/classes"/>
|
||||
</classpath>
|
||||
8
vendor/github.com/camlistore/camlistore/clients/android/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
build
|
||||
gen
|
||||
bin
|
||||
local.properties
|
||||
test/local.properties
|
||||
test/build
|
||||
test/gen
|
||||
test/bin
|
||||
33
vendor/github.com/camlistore/camlistore/clients/android/.project
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>camlistore</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
301
vendor/github.com/camlistore/camlistore/clients/android/.settings/org.eclipse.jdt.core.prefs
generated
vendored
Normal file
@@ -0,0 +1,301 @@
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.codeComplete.argumentPrefixes=
|
||||
org.eclipse.jdt.core.codeComplete.argumentSuffixes=
|
||||
org.eclipse.jdt.core.codeComplete.fieldPrefixes=
|
||||
org.eclipse.jdt.core.codeComplete.fieldSuffixes=
|
||||
org.eclipse.jdt.core.codeComplete.localPrefixes=
|
||||
org.eclipse.jdt.core.codeComplete.localSuffixes=
|
||||
org.eclipse.jdt.core.codeComplete.staticFieldPrefixes=
|
||||
org.eclipse.jdt.core.codeComplete.staticFieldSuffixes=
|
||||
org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes=
|
||||
org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes=
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
|
||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||
org.eclipse.jdt.core.compiler.compliance=1.6
|
||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.source=1.6
|
||||
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_assignment=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
|
||||
org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
|
||||
org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
|
||||
org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_after_package=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_field=0
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_method=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_package=0
|
||||
org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
|
||||
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
|
||||
org.eclipse.jdt.core.formatter.comment.format_block_comments=true
|
||||
org.eclipse.jdt.core.formatter.comment.format_header=false
|
||||
org.eclipse.jdt.core.formatter.comment.format_html=true
|
||||
org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
|
||||
org.eclipse.jdt.core.formatter.comment.format_line_comments=false
|
||||
org.eclipse.jdt.core.formatter.comment.format_source_code=true
|
||||
org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
|
||||
org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
|
||||
org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
|
||||
org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
|
||||
org.eclipse.jdt.core.formatter.comment.line_length=500
|
||||
org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
|
||||
org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
|
||||
org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
|
||||
org.eclipse.jdt.core.formatter.compact_else_if=true
|
||||
org.eclipse.jdt.core.formatter.continuation_indentation=2
|
||||
org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
|
||||
org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
|
||||
org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
|
||||
org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
|
||||
org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
|
||||
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
|
||||
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
|
||||
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
|
||||
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
|
||||
org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
|
||||
org.eclipse.jdt.core.formatter.indent_empty_lines=false
|
||||
org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
|
||||
org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
|
||||
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
|
||||
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
|
||||
org.eclipse.jdt.core.formatter.indentation.size=4
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
|
||||
org.eclipse.jdt.core.formatter.join_lines_in_comments=true
|
||||
org.eclipse.jdt.core.formatter.join_wrapped_lines=false
|
||||
org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
|
||||
org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
|
||||
org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
|
||||
org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
|
||||
org.eclipse.jdt.core.formatter.lineSplit=200
|
||||
org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
|
||||
org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
|
||||
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
|
||||
org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
|
||||
org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
|
||||
org.eclipse.jdt.core.formatter.tabulation.char=space
|
||||
org.eclipse.jdt.core.formatter.tabulation.size=4
|
||||
org.eclipse.jdt.core.formatter.use_on_off_tags=false
|
||||
org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
|
||||
org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
|
||||
org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
|
||||
org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
|
||||
60
vendor/github.com/camlistore/camlistore/clients/android/.settings/org.eclipse.jdt.ui.prefs
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
eclipse.preferences.version=1
|
||||
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
|
||||
formatter_profile=_Camlistore Policy
|
||||
formatter_settings_version=12
|
||||
org.eclipse.jdt.ui.exception.name=e
|
||||
org.eclipse.jdt.ui.gettersetter.use.is=true
|
||||
org.eclipse.jdt.ui.keywordthis=false
|
||||
org.eclipse.jdt.ui.overrideannotation=true
|
||||
sp_cleanup.add_default_serial_version_id=true
|
||||
sp_cleanup.add_generated_serial_version_id=false
|
||||
sp_cleanup.add_missing_annotations=true
|
||||
sp_cleanup.add_missing_deprecated_annotations=true
|
||||
sp_cleanup.add_missing_methods=false
|
||||
sp_cleanup.add_missing_nls_tags=false
|
||||
sp_cleanup.add_missing_override_annotations=true
|
||||
sp_cleanup.add_missing_override_annotations_interface_methods=true
|
||||
sp_cleanup.add_serial_version_id=false
|
||||
sp_cleanup.always_use_blocks=true
|
||||
sp_cleanup.always_use_parentheses_in_expressions=false
|
||||
sp_cleanup.always_use_this_for_non_static_field_access=false
|
||||
sp_cleanup.always_use_this_for_non_static_method_access=false
|
||||
sp_cleanup.convert_to_enhanced_for_loop=false
|
||||
sp_cleanup.correct_indentation=true
|
||||
sp_cleanup.format_source_code=true
|
||||
sp_cleanup.format_source_code_changes_only=false
|
||||
sp_cleanup.make_local_variable_final=false
|
||||
sp_cleanup.make_parameters_final=false
|
||||
sp_cleanup.make_private_fields_final=true
|
||||
sp_cleanup.make_type_abstract_if_missing_method=false
|
||||
sp_cleanup.make_variable_declarations_final=true
|
||||
sp_cleanup.never_use_blocks=false
|
||||
sp_cleanup.never_use_parentheses_in_expressions=true
|
||||
sp_cleanup.on_save_use_additional_actions=true
|
||||
sp_cleanup.organize_imports=true
|
||||
sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
|
||||
sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
|
||||
sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
|
||||
sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
|
||||
sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
|
||||
sp_cleanup.remove_private_constructors=true
|
||||
sp_cleanup.remove_trailing_whitespaces=true
|
||||
sp_cleanup.remove_trailing_whitespaces_all=true
|
||||
sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
|
||||
sp_cleanup.remove_unnecessary_casts=true
|
||||
sp_cleanup.remove_unnecessary_nls_tags=false
|
||||
sp_cleanup.remove_unused_imports=true
|
||||
sp_cleanup.remove_unused_local_variables=false
|
||||
sp_cleanup.remove_unused_private_fields=true
|
||||
sp_cleanup.remove_unused_private_members=false
|
||||
sp_cleanup.remove_unused_private_methods=true
|
||||
sp_cleanup.remove_unused_private_types=true
|
||||
sp_cleanup.sort_members=false
|
||||
sp_cleanup.sort_members_all=false
|
||||
sp_cleanup.use_blocks=false
|
||||
sp_cleanup.use_blocks_only_for_return_and_throw=false
|
||||
sp_cleanup.use_parentheses_in_expressions=false
|
||||
sp_cleanup.use_this_for_non_static_field_access=false
|
||||
sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
|
||||
sp_cleanup.use_this_for_non_static_method_access=false
|
||||
sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
|
||||
74
vendor/github.com/camlistore/camlistore/clients/android/AndroidManifest.xml
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.camlistore"
|
||||
android:versionCode="2"
|
||||
android:versionName="0.6.1">
|
||||
|
||||
<!-- Now using Gingerbread and up.... -->
|
||||
<uses-sdk android:minSdkVersion="10" android:targetSdkVersion="17" />
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.BATTERY_STATS" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<application android:icon="@drawable/icon" android:label="@string/app_name"
|
||||
android:name=".UploadApplication" android:allowBackup="true">
|
||||
|
||||
<service android:name=".UploadService"
|
||||
android:exported="false"
|
||||
android:label="Camlistore Upload Service" />
|
||||
|
||||
<activity android:name=".CamliActivity"
|
||||
android:label="@string/app_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<data android:mimeType="*/*" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||
<data android:mimeType="*/*" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name=".BrowseActivity">
|
||||
</activity>
|
||||
|
||||
<activity android:name=".SettingsActivity">
|
||||
</activity>
|
||||
|
||||
<receiver android:name=".OnBootReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".OnAlarmReceiver">
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".WifiPowerReceiver"
|
||||
android:enabled="true"
|
||||
android:priority="0">
|
||||
<intent-filter>
|
||||
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
</application>
|
||||
</manifest>
|
||||
17
vendor/github.com/camlistore/camlistore/clients/android/Makefile
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
all:
|
||||
./check-environment.pl
|
||||
ant debug
|
||||
|
||||
# Dummy target to make build.pl happy
|
||||
install:
|
||||
./check-environment.pl
|
||||
ant debug
|
||||
|
||||
env:
|
||||
docker build -t camlistore/android devenv
|
||||
|
||||
dockerdebug:
|
||||
docker run -v $(GOPATH)/src/camlistore.org:/src/camlistore.org camlistore/android /src/camlistore.org/clients/android/build-in-docker.pl debug
|
||||
|
||||
dockerrelease:
|
||||
docker run -i -t -v $(GOPATH)/src/camlistore.org:/src/camlistore.org -v $(HOME)/keys/android-camlistore:/keys camlistore/android /src/camlistore.org/clients/android/build-in-docker.pl release
|
||||
64
vendor/github.com/camlistore/camlistore/clients/android/build-in-docker.pl
generated
vendored
Executable file
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use File::Path qw(make_path);
|
||||
|
||||
die "This script is meant to be run within the camlistore/android Docker contain. Run 'make env' to build it.\n"
|
||||
unless $ENV{IN_DOCKER};
|
||||
|
||||
my $mode = shift || "debug";
|
||||
|
||||
my $ANDROID = "/src/camlistore.org/clients/android";
|
||||
my $ASSETS = "$ANDROID/assets";
|
||||
my $GENDIR = "$ANDROID/gen/org/camlistore";
|
||||
|
||||
umask 0;
|
||||
make_path($GENDIR, { mode => 0755 }) unless -d $GENDIR;
|
||||
|
||||
$ENV{GOROOT} = "/usr/local/go";
|
||||
$ENV{GOBIN} = $GENDIR;
|
||||
$ENV{GOPATH} = "/";
|
||||
$ENV{GOARCH} = "arm";
|
||||
print "Building ARM camlistore.org/cmd/camput\n";
|
||||
system("/usr/local/go/bin/go", "install", "camlistore.org/cmd/camput")
|
||||
and die "Failed to build camput";
|
||||
|
||||
system("cp", "-p", "$GENDIR/linux_arm/camput", "$ASSETS/camput.arm")
|
||||
and die "cp failure";
|
||||
# TODO: build an x86 version too? if/when those Android devices matter.
|
||||
|
||||
{
|
||||
open(my $vfh, ">$ASSETS/camput-version.txt") or die "open camput-version error: $!";
|
||||
# TODO(bradfitz): make these values automatic, and don't make the
|
||||
# "Version" menu say "camput version" when it runs. Also maybe
|
||||
# keep a history of these somewhere more convenient.
|
||||
print $vfh "app 0.6.1 camput ccacf764 go 70499e5fbe5b";
|
||||
}
|
||||
|
||||
chdir $ASSETS or die "can't cd to assets dir";
|
||||
|
||||
my $digest = `openssl sha1 camput.arm`;
|
||||
chomp $digest;
|
||||
print "ARM camput is $digest\n";
|
||||
die "No digest" unless $digest;
|
||||
write_file("$GENDIR/ChildProcessConfig.java", "package org.camlistore; public final class ChildProcessConfig { // $digest\n}");
|
||||
|
||||
print "Running ant $mode\n";
|
||||
chdir $ANDROID or die "can't cd to android dir";
|
||||
exec "ant",
|
||||
"-Dsdk.dir=/usr/local/android-sdk-linux",
|
||||
"-Dkey.store=/keys/android-camlistore.keystore",
|
||||
"-Dkey.alias=camkey",
|
||||
$mode;
|
||||
|
||||
sub write_file {
|
||||
my ($file, $contents) = @_;
|
||||
if (open(my $fh, $file)) {
|
||||
my $cur = do { local $/; <$fh> };
|
||||
return if $cur eq $contents;
|
||||
}
|
||||
open(my $fh, ">$file") or die "Failed to open $file: $!";
|
||||
print $fh $contents;
|
||||
close($fh) or die "Close: $!";
|
||||
print "Wrote $file\n";
|
||||
}
|
||||
2
vendor/github.com/camlistore/camlistore/clients/android/build.properties
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
out.dir=build
|
||||
gen.dir=build/gen
|
||||
72
vendor/github.com/camlistore/camlistore/clients/android/build.xml
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="CamliActivity" default="help">
|
||||
|
||||
<!-- The local.properties file is created and updated by the 'android' tool.
|
||||
It contains the path to the SDK. It should *NOT* be checked in in Version
|
||||
Control Systems. -->
|
||||
<property file="local.properties" />
|
||||
|
||||
<!-- The build.properties file can be created by you and is never touched
|
||||
by the 'android' tool. This is the place to change some of the default property values
|
||||
used by the Ant rules.
|
||||
Here are some properties you may want to change/update:
|
||||
|
||||
application.package
|
||||
the name of your application package as defined in the manifest. Used by the
|
||||
'uninstall' rule.
|
||||
source.dir
|
||||
the name of the source directory. Default is 'src'.
|
||||
out.dir
|
||||
the name of the output directory. Default is 'bin'.
|
||||
|
||||
Properties related to the SDK location or the project target should be updated
|
||||
using the 'android' tool with the 'update' action.
|
||||
|
||||
This file is an integral part of the build system for your application and
|
||||
should be checked in in Version Control Systems.
|
||||
|
||||
-->
|
||||
<property file="build.properties" />
|
||||
|
||||
<!-- The default.properties file is created and updated by the 'android' tool, as well
|
||||
as ADT.
|
||||
This file is an integral part of the build system for your application and
|
||||
should be checked in in Version Control Systems. -->
|
||||
<property file="default.properties" />
|
||||
|
||||
<!--
|
||||
Import per project custom build rules if present at the root of the project.
|
||||
This is the place to put custom intermediary targets such as:
|
||||
-pre-build
|
||||
-pre-compile
|
||||
-post-compile (This is typically used for code obfuscation.
|
||||
Compiled code location: ${out.classes.absolute.dir}
|
||||
If this is not done in place, override ${out.dex.input.absolute.dir})
|
||||
-post-package
|
||||
-post-build
|
||||
-pre-clean
|
||||
-->
|
||||
<import file="custom_rules.xml" optional="true" />
|
||||
|
||||
<!-- Import the actual build file.
|
||||
|
||||
To customize existing targets, there are two options:
|
||||
- Customize only one target:
|
||||
- copy/paste the target into this file, *before* the
|
||||
<import> task.
|
||||
- customize it to your needs.
|
||||
- Customize the whole content of build.xml
|
||||
- copy/paste the content of the rules files (minus the top node)
|
||||
into this file, replacing the <import> task.
|
||||
- customize to your needs.
|
||||
|
||||
***********************
|
||||
****** IMPORTANT ******
|
||||
***********************
|
||||
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
|
||||
in order to avoid having your file be overridden by tools such as "android update project"
|
||||
-->
|
||||
<!-- version-tag: 1 -->
|
||||
<import file="${sdk.dir}/tools/ant/build.xml" />
|
||||
|
||||
</project>
|
||||
14
vendor/github.com/camlistore/camlistore/clients/android/check-environment.pl
generated
vendored
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use FindBin qw($Bin);
|
||||
|
||||
my $props = "$Bin/local.properties";
|
||||
unless (-e $props) {
|
||||
die "\n".
|
||||
"**************************************************************\n".
|
||||
"Can't build the Camlistore Android client; SDK not configured.\n".
|
||||
"You need to create your $props file.\n".
|
||||
"See local.properties.TEMPLATE for instructions.\n".
|
||||
"**************************************************************\n\n";
|
||||
}
|
||||
12
vendor/github.com/camlistore/camlistore/clients/android/default.properties
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# This file is automatically generated by Android Tools.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file must be checked in Version Control Systems.
|
||||
#
|
||||
# To customize properties used by the Ant build system use,
|
||||
# "build.properties", and override values to adapt the script to your
|
||||
# project structure.
|
||||
|
||||
# Project target.
|
||||
target=android-17
|
||||
|
||||
38
vendor/github.com/camlistore/camlistore/clients/android/devenv/Dockerfile
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
# Build environment in which to build the Camlistore Android app.
|
||||
#
|
||||
# This extends the Dockerfile from https://index.docker.io/u/wasabeef/android/
|
||||
|
||||
FROM wasabeef/android
|
||||
MAINTAINER bradfitz <brad@danga.com>
|
||||
|
||||
# Found these from: android list sdk -u -e
|
||||
RUN android list sdk -u -e | grep build-tools- | perl -npe 's/.+"(.+)"/$1/' > /tmp/build-tools-version
|
||||
RUN perl -e 'die "No Android build tools version found." unless -s "/tmp/build-tools-version"'
|
||||
RUN echo y | android update sdk -u -t $(cat /tmp/build-tools-version)
|
||||
RUN echo y | android update sdk -u -t android-17
|
||||
|
||||
# Don't need mercurial yet, since we're just using the archive URL to fetch Go.
|
||||
# But it's possible we may want to switch to using hg, in which case:
|
||||
# RUN yum -y install mercurial
|
||||
|
||||
# Update the GOVERS to depend on a new version of Go.
|
||||
#
|
||||
# The 073fc578434b version is Go 1.3.1 (2014-02-21),
|
||||
# to satisfy the dependency for Go 1.3 in the Docker build of
|
||||
# camput.
|
||||
ENV GOVERS 073fc578434b
|
||||
|
||||
RUN cd /usr/local && curl -O http://go.googlecode.com/archive/$GOVERS.zip
|
||||
RUN cd /usr/local && unzip -q $GOVERS.zip
|
||||
RUN cd /usr/local && mv go-$GOVERS go
|
||||
RUN chmod 0755 /usr/local/go/src/make.bash
|
||||
RUN echo $GOVERS > /usr/local/go/VERSION
|
||||
RUN GOROOT=/usr/local/go GOARCH=arm bash -c "cd /usr/local/go/src && ./make.bash"
|
||||
|
||||
|
||||
ENV ANDROID_HOME /usr/local/android-sdk-linux
|
||||
ENV ANT_HOME /usr/local/apache-ant-1.9.2
|
||||
ENV PATH $PATH:$ANDROID_HOME/tools
|
||||
ENV PATH $PATH:$ANDROID_HOME/platform-tools
|
||||
ENV PATH $PATH:$ANT_HOME/bin
|
||||
ENV IN_DOCKER 1
|
||||
3
vendor/github.com/camlistore/camlistore/clients/android/local.properties.TEMPLATE
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# Copy this file to one named "local.properties" and update this path to
|
||||
# wherever your Android SDK is located:
|
||||
sdk.dir=/home/bradfitz/sdk/android
|
||||
14
vendor/github.com/camlistore/camlistore/clients/android/project.properties
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
# This file is automatically generated by Android Tools.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file must be checked in Version Control Systems.
|
||||
#
|
||||
# To customize properties used by the Ant build system edit
|
||||
# "ant.properties", and override values to adapt the script to your
|
||||
# project structure.
|
||||
#
|
||||
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||
|
||||
# Project target.
|
||||
target=android-17
|
||||
BIN
vendor/github.com/camlistore/camlistore/clients/android/res/drawable-hdpi/icon.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
vendor/github.com/camlistore/camlistore/clients/android/res/drawable-mdpi/icon.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
vendor/github.com/camlistore/camlistore/clients/android/res/drawable-xhdpi/icon.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
vendor/github.com/camlistore/camlistore/clients/android/res/drawable/icon_file.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 640 B |
BIN
vendor/github.com/camlistore/camlistore/clients/android/res/drawable/icon_folder.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
77
vendor/github.com/camlistore/camlistore/clients/android/res/layout/main.xml
generated
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="10sp" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textStatus"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Camlistore Uploader" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="horizontal"
|
||||
android:padding="5px" >
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonToggle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="15sp"
|
||||
android:text="@+string/pause_resume" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textBlobsRemain"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@+string/blobs_remaining" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textUploadStatus"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@+string/status_detail" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textByteStatus"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@+string/bytes_uploaded" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressByteStatus"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="20sp"
|
||||
android:indeterminate="false"
|
||||
android:indeterminateOnly="false"
|
||||
android:minHeight="20sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textFileStatus"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@+string/files_uploaded" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressFileStatus"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="20sp"
|
||||
android:indeterminate="false"
|
||||
android:indeterminateOnly="false"
|
||||
android:minHeight="20sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textStats"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="" />
|
||||
|
||||
</LinearLayout>
|
||||
39
vendor/github.com/camlistore/camlistore/clients/android/res/values/strings.xml
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="app_name">Camlistore Uploader</string>
|
||||
<string name="settings_confirmation_dialog_title">Use these settings?</string>
|
||||
<string name="settings_host_title">Camlistore server</string>
|
||||
<string name="settings_host_summary">e.g. https://foo.example.com or "example.com:3179"</string>
|
||||
<string name="settings_qr_title">QR</string>
|
||||
<string name="settings_qr_summary">Scan QR code from /ui/mobile.html</string>
|
||||
<string name="settings_trusted_cert_title">Self-signed cert fingerprint</string>
|
||||
<string name="settings_trusted_cert_summary">The fingerprint of your self-signed certificate. Not needed for commercial certs.</string>
|
||||
<string name="settings_username_title">Username</string>
|
||||
<string name="settings_password_title">Password</string>
|
||||
<string name="settings_max_cache_size_title">Maximum cache size</string>
|
||||
<string name="settings_max_cache_size_summary">%d MB</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="pause_resume">Pause / Resume</string>
|
||||
<string name="pause">Pause</string>
|
||||
<string name="resume">Resume</string>
|
||||
<string name="stop">Stop</string>
|
||||
<string name="stop_die">Force-kill all</string>
|
||||
<string name="version">Version</string>
|
||||
<string name="uploading">Uploading…</string>
|
||||
<string name="digesting">Digesting….</string>
|
||||
<string name="settings_auto">Auto-Upload</string>
|
||||
<string name="settings_auto_summary">Upload SD card files as created</string>
|
||||
<string name="settings_dev_ip">Development IP address</string>
|
||||
<string name="files_remaining">Files Remaining: —</string>
|
||||
<string name="status_detail">[status detail]</string>
|
||||
<string name="files_uploaded">Files Uploaded</string>
|
||||
<string name="bytes_uploaded">Bytes Uploaded</string>
|
||||
<string name="settings">Settings</string>
|
||||
<string name="upload_all">Upload All</string>
|
||||
<string name="browse">Browse</string>
|
||||
<string name="results">Results</string>
|
||||
<string name="settings_auto_required_ssid">Required SSID</string>
|
||||
|
||||
</resources>
|
||||
83
vendor/github.com/camlistore/camlistore/clients/android/res/xml/preferences.xml
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:key="first_preferencescreen" >
|
||||
|
||||
<org.camlistore.QRPreference
|
||||
android:key="camli.qr"
|
||||
android:summary="@string/settings_qr_summary"
|
||||
android:title="@string/settings_qr_title"/>
|
||||
<EditTextPreference
|
||||
android:key="camli.host"
|
||||
android:persistent="true"
|
||||
android:summary="@string/settings_host_summary"
|
||||
android:title="@string/settings_host_title" />
|
||||
<EditTextPreference
|
||||
android:key="camli.username"
|
||||
android:persistent="true"
|
||||
android:title="@string/settings_username_title" />
|
||||
<EditTextPreference
|
||||
android:inputType="textPassword"
|
||||
android:key="camli.password"
|
||||
android:persistent="true"
|
||||
android:title="@string/settings_password_title" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:key="camli.auto"
|
||||
android:persistent="true"
|
||||
android:summary="@string/settings_auto_summary"
|
||||
android:title="@string/settings_auto" />
|
||||
|
||||
<PreferenceScreen
|
||||
android:key="camli.auto.opts"
|
||||
android:title="Auto-upload settings" >
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="true"
|
||||
android:key="camli.auto.photos"
|
||||
android:persistent="true"
|
||||
android:title="Photos (DCIM/Camera/)" />
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="true"
|
||||
android:key="camli.auto.mytracks"
|
||||
android:persistent="true"
|
||||
android:title="MyTracks exports" />
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="false"
|
||||
android:key="camli.auto.require_wifi"
|
||||
android:persistent="true"
|
||||
android:summary="Wait for Wifi to auto-upload"
|
||||
android:title="Require Wifi" />
|
||||
<EditTextPreference
|
||||
android:key="camli.auto.required_wifi_ssid"
|
||||
android:persistent="true"
|
||||
android:singleLine="true"
|
||||
android:summary="Restrict auto-upload to this SSID"
|
||||
android:title="@string/settings_auto_required_ssid" />
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="false"
|
||||
android:key="camli.auto.require_power"
|
||||
android:persistent="true"
|
||||
android:summary="Wait until charging to auto-upload"
|
||||
android:title="Require Power" />
|
||||
</PreferenceScreen>
|
||||
|
||||
<EditTextPreference
|
||||
android:key="camli.max_cache_mb"
|
||||
android:numeric="integer"
|
||||
android:persistent="true"
|
||||
android:singleLine="true"
|
||||
android:title="@string/settings_max_cache_size_title" />
|
||||
|
||||
<EditTextPreference
|
||||
android:key="camli.trusted_cert"
|
||||
android:persistent="true"
|
||||
android:summary="@string/settings_trusted_cert_summary"
|
||||
android:title="@string/settings_trusted_cert_title" />
|
||||
|
||||
<EditTextPreference
|
||||
android:key="camli.dev_ip"
|
||||
android:phoneNumber="true"
|
||||
android:persistent="true"
|
||||
android:singleLine="true"
|
||||
android:title="@string/settings_dev_ip" />
|
||||
|
||||
</PreferenceScreen>
|
||||
506
vendor/github.com/camlistore/camlistore/clients/android/src/com/google/zxing/integration/android/IntentIntegrator.java
generated
vendored
Normal file
@@ -0,0 +1,506 @@
|
||||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.integration.android;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Fragment;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* <p>A utility class which helps ease integration with Barcode Scanner via {@link Intent}s. This is a simple
|
||||
* way to invoke barcode scanning and receive the result, without any need to integrate, modify, or learn the
|
||||
* project's source code.</p>
|
||||
*
|
||||
* <h2>Initiating a barcode scan</h2>
|
||||
*
|
||||
* <p>To integrate, create an instance of {@code IntentIntegrator} and call {@link #initiateScan()} and wait
|
||||
* for the result in your app.</p>
|
||||
*
|
||||
* <p>It does require that the Barcode Scanner (or work-alike) application is installed. The
|
||||
* {@link #initiateScan()} method will prompt the user to download the application, if needed.</p>
|
||||
*
|
||||
* <p>There are a few steps to using this integration. First, your {@link Activity} must implement
|
||||
* the method {@link Activity#onActivityResult(int, int, Intent)} and include a line of code like this:</p>
|
||||
*
|
||||
* <pre>{@code
|
||||
* public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
* IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
|
||||
* if (scanResult != null) {
|
||||
* // handle scan result
|
||||
* }
|
||||
* // else continue with any other code you need in the method
|
||||
* ...
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <p>This is where you will handle a scan result.</p>
|
||||
*
|
||||
* <p>Second, just call this in response to a user action somewhere to begin the scan process:</p>
|
||||
*
|
||||
* <pre>{@code
|
||||
* IntentIntegrator integrator = new IntentIntegrator(yourActivity);
|
||||
* integrator.initiateScan();
|
||||
* }</pre>
|
||||
*
|
||||
* <p>Note that {@link #initiateScan()} returns an {@link AlertDialog} which is non-null if the
|
||||
* user was prompted to download the application. This lets the calling app potentially manage the dialog.
|
||||
* In particular, ideally, the app dismisses the dialog if it's still active in its {@link Activity#onPause()}
|
||||
* method.</p>
|
||||
*
|
||||
* <p>You can use {@link #setTitle(String)} to customize the title of this download prompt dialog (or, use
|
||||
* {@link #setTitleByID(int)} to set the title by string resource ID.) Likewise, the prompt message, and
|
||||
* yes/no button labels can be changed.</p>
|
||||
*
|
||||
* <p>Finally, you can use {@link #addExtra(String, Object)} to add more parameters to the Intent used
|
||||
* to invoke the scanner. This can be used to set additional options not directly exposed by this
|
||||
* simplified API.</p>
|
||||
*
|
||||
* <p>By default, this will only allow applications that are known to respond to this intent correctly
|
||||
* do so. The apps that are allowed to response can be set with {@link #setTargetApplications(List)}.
|
||||
* For example, set to {@link #TARGET_BARCODE_SCANNER_ONLY} to only target the Barcode Scanner app itself.</p>
|
||||
*
|
||||
* <h2>Sharing text via barcode</h2>
|
||||
*
|
||||
* <p>To share text, encoded as a QR Code on-screen, similarly, see {@link #shareText(CharSequence)}.</p>
|
||||
*
|
||||
* <p>Some code, particularly download integration, was contributed from the Anobiit application.</p>
|
||||
*
|
||||
* <h2>Enabling experimental barcode formats</h2>
|
||||
*
|
||||
* <p>Some formats are not enabled by default even when scanning with {@link #ALL_CODE_TYPES}, such as
|
||||
* PDF417. Use {@link #initiateScan(java.util.Collection)} with
|
||||
* a collection containing the names of formats to scan for explicitly, like "PDF_417", to use such
|
||||
* formats.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @author Fred Lin
|
||||
* @author Isaac Potoczny-Jones
|
||||
* @author Brad Drehmer
|
||||
* @author gcstang
|
||||
*/
|
||||
public class IntentIntegrator {
|
||||
|
||||
public static final int REQUEST_CODE = 0x0000c0de; // Only use bottom 16 bits
|
||||
private static final String TAG = IntentIntegrator.class.getSimpleName();
|
||||
|
||||
public static final String DEFAULT_TITLE = "Install Barcode Scanner?";
|
||||
public static final String DEFAULT_MESSAGE =
|
||||
"This application requires Barcode Scanner. Would you like to install it?";
|
||||
public static final String DEFAULT_YES = "Yes";
|
||||
public static final String DEFAULT_NO = "No";
|
||||
|
||||
private static final String BS_PACKAGE = "com.google.zxing.client.android";
|
||||
private static final String BSPLUS_PACKAGE = "com.srowen.bs.android";
|
||||
|
||||
// supported barcode formats
|
||||
public static final Collection<String> PRODUCT_CODE_TYPES = list("UPC_A", "UPC_E", "EAN_8", "EAN_13", "RSS_14");
|
||||
public static final Collection<String> ONE_D_CODE_TYPES =
|
||||
list("UPC_A", "UPC_E", "EAN_8", "EAN_13", "CODE_39", "CODE_93", "CODE_128",
|
||||
"ITF", "RSS_14", "RSS_EXPANDED");
|
||||
public static final Collection<String> QR_CODE_TYPES = Collections.singleton("QR_CODE");
|
||||
public static final Collection<String> DATA_MATRIX_TYPES = Collections.singleton("DATA_MATRIX");
|
||||
|
||||
public static final Collection<String> ALL_CODE_TYPES = null;
|
||||
|
||||
public static final List<String> TARGET_BARCODE_SCANNER_ONLY = Collections.singletonList(BS_PACKAGE);
|
||||
public static final List<String> TARGET_ALL_KNOWN = list(
|
||||
BSPLUS_PACKAGE, // Barcode Scanner+
|
||||
BSPLUS_PACKAGE + ".simple", // Barcode Scanner+ Simple
|
||||
BS_PACKAGE // Barcode Scanner
|
||||
// What else supports this intent?
|
||||
);
|
||||
|
||||
private final Activity activity;
|
||||
private final Fragment fragment;
|
||||
|
||||
private String title;
|
||||
private String message;
|
||||
private String buttonYes;
|
||||
private String buttonNo;
|
||||
private List<String> targetApplications;
|
||||
private final Map<String,Object> moreExtras = new HashMap<String,Object>(3);
|
||||
|
||||
/**
|
||||
* @param activity {@link Activity} invoking the integration
|
||||
*/
|
||||
public IntentIntegrator(Activity activity) {
|
||||
this.activity = activity;
|
||||
this.fragment = null;
|
||||
initializeConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fragment {@link Fragment} invoking the integration.
|
||||
* {@link #startActivityForResult(Intent, int)} will be called on the {@link Fragment} instead
|
||||
* of an {@link Activity}
|
||||
*/
|
||||
public IntentIntegrator(Fragment fragment) {
|
||||
this.activity = fragment.getActivity();
|
||||
this.fragment = fragment;
|
||||
initializeConfiguration();
|
||||
}
|
||||
|
||||
private void initializeConfiguration() {
|
||||
title = DEFAULT_TITLE;
|
||||
message = DEFAULT_MESSAGE;
|
||||
buttonYes = DEFAULT_YES;
|
||||
buttonNo = DEFAULT_NO;
|
||||
targetApplications = TARGET_ALL_KNOWN;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public void setTitleByID(int titleID) {
|
||||
title = activity.getString(titleID);
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public void setMessageByID(int messageID) {
|
||||
message = activity.getString(messageID);
|
||||
}
|
||||
|
||||
public String getButtonYes() {
|
||||
return buttonYes;
|
||||
}
|
||||
|
||||
public void setButtonYes(String buttonYes) {
|
||||
this.buttonYes = buttonYes;
|
||||
}
|
||||
|
||||
public void setButtonYesByID(int buttonYesID) {
|
||||
buttonYes = activity.getString(buttonYesID);
|
||||
}
|
||||
|
||||
public String getButtonNo() {
|
||||
return buttonNo;
|
||||
}
|
||||
|
||||
public void setButtonNo(String buttonNo) {
|
||||
this.buttonNo = buttonNo;
|
||||
}
|
||||
|
||||
public void setButtonNoByID(int buttonNoID) {
|
||||
buttonNo = activity.getString(buttonNoID);
|
||||
}
|
||||
|
||||
public Collection<String> getTargetApplications() {
|
||||
return targetApplications;
|
||||
}
|
||||
|
||||
public final void setTargetApplications(List<String> targetApplications) {
|
||||
if (targetApplications.isEmpty()) {
|
||||
throw new IllegalArgumentException("No target applications");
|
||||
}
|
||||
this.targetApplications = targetApplications;
|
||||
}
|
||||
|
||||
public void setSingleTargetApplication(String targetApplication) {
|
||||
this.targetApplications = Collections.singletonList(targetApplication);
|
||||
}
|
||||
|
||||
public Map<String,?> getMoreExtras() {
|
||||
return moreExtras;
|
||||
}
|
||||
|
||||
public final void addExtra(String key, Object value) {
|
||||
moreExtras.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates a scan for all known barcode types with the default camera.
|
||||
*
|
||||
* @return the {@link AlertDialog} that was shown to the user prompting them to download the app
|
||||
* if a prompt was needed, or null otherwise.
|
||||
*/
|
||||
public final AlertDialog initiateScan() {
|
||||
return initiateScan(ALL_CODE_TYPES, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates a scan for all known barcode types with the specified camera.
|
||||
*
|
||||
* @param cameraId camera ID of the camera to use. A negative value means "no preference".
|
||||
* @return the {@link AlertDialog} that was shown to the user prompting them to download the app
|
||||
* if a prompt was needed, or null otherwise.
|
||||
*/
|
||||
public final AlertDialog initiateScan(int cameraId) {
|
||||
return initiateScan(ALL_CODE_TYPES, cameraId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates a scan, using the default camera, only for a certain set of barcode types, given as strings corresponding
|
||||
* to their names in ZXing's {@code BarcodeFormat} class like "UPC_A". You can supply constants
|
||||
* like {@link #PRODUCT_CODE_TYPES} for example.
|
||||
*
|
||||
* @param desiredBarcodeFormats names of {@code BarcodeFormat}s to scan for
|
||||
* @return the {@link AlertDialog} that was shown to the user prompting them to download the app
|
||||
* if a prompt was needed, or null otherwise.
|
||||
*/
|
||||
public final AlertDialog initiateScan(Collection<String> desiredBarcodeFormats) {
|
||||
return initiateScan(desiredBarcodeFormats, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates a scan, using the specified camera, only for a certain set of barcode types, given as strings corresponding
|
||||
* to their names in ZXing's {@code BarcodeFormat} class like "UPC_A". You can supply constants
|
||||
* like {@link #PRODUCT_CODE_TYPES} for example.
|
||||
*
|
||||
* @param desiredBarcodeFormats names of {@code BarcodeFormat}s to scan for
|
||||
* @param cameraId camera ID of the camera to use. A negative value means "no preference".
|
||||
* @return the {@link AlertDialog} that was shown to the user prompting them to download the app
|
||||
* if a prompt was needed, or null otherwise
|
||||
*/
|
||||
public final AlertDialog initiateScan(Collection<String> desiredBarcodeFormats, int cameraId) {
|
||||
Intent intentScan = new Intent(BS_PACKAGE + ".SCAN");
|
||||
intentScan.addCategory(Intent.CATEGORY_DEFAULT);
|
||||
|
||||
// check which types of codes to scan for
|
||||
if (desiredBarcodeFormats != null) {
|
||||
// set the desired barcode types
|
||||
StringBuilder joinedByComma = new StringBuilder();
|
||||
for (String format : desiredBarcodeFormats) {
|
||||
if (joinedByComma.length() > 0) {
|
||||
joinedByComma.append(',');
|
||||
}
|
||||
joinedByComma.append(format);
|
||||
}
|
||||
intentScan.putExtra("SCAN_FORMATS", joinedByComma.toString());
|
||||
}
|
||||
|
||||
// check requested camera ID
|
||||
if (cameraId >= 0) {
|
||||
intentScan.putExtra("SCAN_CAMERA_ID", cameraId);
|
||||
}
|
||||
|
||||
String targetAppPackage = findTargetAppPackage(intentScan);
|
||||
if (targetAppPackage == null) {
|
||||
return showDownloadDialog();
|
||||
}
|
||||
intentScan.setPackage(targetAppPackage);
|
||||
intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||
attachMoreExtras(intentScan);
|
||||
startActivityForResult(intentScan, REQUEST_CODE);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start an activity. This method is defined to allow different methods of activity starting for
|
||||
* newer versions of Android and for compatibility library.
|
||||
*
|
||||
* @param intent Intent to start.
|
||||
* @param code Request code for the activity
|
||||
* @see android.app.Activity#startActivityForResult(Intent, int)
|
||||
* @see android.app.Fragment#startActivityForResult(Intent, int)
|
||||
*/
|
||||
protected void startActivityForResult(Intent intent, int code) {
|
||||
if (fragment == null) {
|
||||
activity.startActivityForResult(intent, code);
|
||||
} else {
|
||||
fragment.startActivityForResult(intent, code);
|
||||
}
|
||||
}
|
||||
|
||||
private String findTargetAppPackage(Intent intent) {
|
||||
PackageManager pm = activity.getPackageManager();
|
||||
List<ResolveInfo> availableApps = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
|
||||
if (availableApps != null) {
|
||||
for (String targetApp : targetApplications) {
|
||||
if (contains(availableApps, targetApp)) {
|
||||
return targetApp;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean contains(Iterable<ResolveInfo> availableApps, String targetApp) {
|
||||
for (ResolveInfo availableApp : availableApps) {
|
||||
String packageName = availableApp.activityInfo.packageName;
|
||||
if (targetApp.equals(packageName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private AlertDialog showDownloadDialog() {
|
||||
AlertDialog.Builder downloadDialog = new AlertDialog.Builder(activity);
|
||||
downloadDialog.setTitle(title);
|
||||
downloadDialog.setMessage(message);
|
||||
downloadDialog.setPositiveButton(buttonYes, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
String packageName;
|
||||
if (targetApplications.contains(BS_PACKAGE)) {
|
||||
// Prefer to suggest download of BS if it's anywhere in the list
|
||||
packageName = BS_PACKAGE;
|
||||
} else {
|
||||
// Otherwise, first option:
|
||||
packageName = targetApplications.get(0);
|
||||
}
|
||||
Uri uri = Uri.parse("market://details?id=" + packageName);
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
||||
try {
|
||||
if (fragment == null) {
|
||||
activity.startActivity(intent);
|
||||
} else {
|
||||
fragment.startActivity(intent);
|
||||
}
|
||||
} catch (ActivityNotFoundException anfe) {
|
||||
// Hmm, market is not installed
|
||||
Log.w(TAG, "Google Play is not installed; cannot install " + packageName);
|
||||
}
|
||||
}
|
||||
});
|
||||
downloadDialog.setNegativeButton(buttonNo, null);
|
||||
downloadDialog.setCancelable(true);
|
||||
return downloadDialog.show();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>Call this from your {@link Activity}'s
|
||||
* {@link Activity#onActivityResult(int, int, Intent)} method.</p>
|
||||
*
|
||||
* @param requestCode request code from {@code onActivityResult()}
|
||||
* @param resultCode result code from {@code onActivityResult()}
|
||||
* @param intent {@link Intent} from {@code onActivityResult()}
|
||||
* @return null if the event handled here was not related to this class, or
|
||||
* else an {@link IntentResult} containing the result of the scan. If the user cancelled scanning,
|
||||
* the fields will be null.
|
||||
*/
|
||||
public static IntentResult parseActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
if (requestCode == REQUEST_CODE) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
String contents = intent.getStringExtra("SCAN_RESULT");
|
||||
String formatName = intent.getStringExtra("SCAN_RESULT_FORMAT");
|
||||
byte[] rawBytes = intent.getByteArrayExtra("SCAN_RESULT_BYTES");
|
||||
int intentOrientation = intent.getIntExtra("SCAN_RESULT_ORIENTATION", Integer.MIN_VALUE);
|
||||
Integer orientation = intentOrientation == Integer.MIN_VALUE ? null : intentOrientation;
|
||||
String errorCorrectionLevel = intent.getStringExtra("SCAN_RESULT_ERROR_CORRECTION_LEVEL");
|
||||
return new IntentResult(contents,
|
||||
formatName,
|
||||
rawBytes,
|
||||
orientation,
|
||||
errorCorrectionLevel);
|
||||
}
|
||||
return new IntentResult();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Defaults to type "TEXT_TYPE".
|
||||
*
|
||||
* @param text the text string to encode as a barcode
|
||||
* @return the {@link AlertDialog} that was shown to the user prompting them to download the app
|
||||
* if a prompt was needed, or null otherwise
|
||||
* @see #shareText(CharSequence, CharSequence)
|
||||
*/
|
||||
public final AlertDialog shareText(CharSequence text) {
|
||||
return shareText(text, "TEXT_TYPE");
|
||||
}
|
||||
|
||||
/**
|
||||
* Shares the given text by encoding it as a barcode, such that another user can
|
||||
* scan the text off the screen of the device.
|
||||
*
|
||||
* @param text the text string to encode as a barcode
|
||||
* @param type type of data to encode. See {@code com.google.zxing.client.android.Contents.Type} constants.
|
||||
* @return the {@link AlertDialog} that was shown to the user prompting them to download the app
|
||||
* if a prompt was needed, or null otherwise
|
||||
*/
|
||||
public final AlertDialog shareText(CharSequence text, CharSequence type) {
|
||||
Intent intent = new Intent();
|
||||
intent.addCategory(Intent.CATEGORY_DEFAULT);
|
||||
intent.setAction(BS_PACKAGE + ".ENCODE");
|
||||
intent.putExtra("ENCODE_TYPE", type);
|
||||
intent.putExtra("ENCODE_DATA", text);
|
||||
String targetAppPackage = findTargetAppPackage(intent);
|
||||
if (targetAppPackage == null) {
|
||||
return showDownloadDialog();
|
||||
}
|
||||
intent.setPackage(targetAppPackage);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||
attachMoreExtras(intent);
|
||||
if (fragment == null) {
|
||||
activity.startActivity(intent);
|
||||
} else {
|
||||
fragment.startActivity(intent);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static List<String> list(String... values) {
|
||||
return Collections.unmodifiableList(Arrays.asList(values));
|
||||
}
|
||||
|
||||
private void attachMoreExtras(Intent intent) {
|
||||
for (Map.Entry<String,Object> entry : moreExtras.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
// Kind of hacky
|
||||
if (value instanceof Integer) {
|
||||
intent.putExtra(key, (Integer) value);
|
||||
} else if (value instanceof Long) {
|
||||
intent.putExtra(key, (Long) value);
|
||||
} else if (value instanceof Boolean) {
|
||||
intent.putExtra(key, (Boolean) value);
|
||||
} else if (value instanceof Double) {
|
||||
intent.putExtra(key, (Double) value);
|
||||
} else if (value instanceof Float) {
|
||||
intent.putExtra(key, (Float) value);
|
||||
} else if (value instanceof Bundle) {
|
||||
intent.putExtra(key, (Bundle) value);
|
||||
} else {
|
||||
intent.putExtra(key, value.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
95
vendor/github.com/camlistore/camlistore/clients/android/src/com/google/zxing/integration/android/IntentResult.java
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.integration.android;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates the result of a barcode scan invoked through {@link IntentIntegrator}.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class IntentResult {
|
||||
|
||||
private final String contents;
|
||||
private final String formatName;
|
||||
private final byte[] rawBytes;
|
||||
private final Integer orientation;
|
||||
private final String errorCorrectionLevel;
|
||||
|
||||
IntentResult() {
|
||||
this(null, null, null, null, null);
|
||||
}
|
||||
|
||||
IntentResult(String contents,
|
||||
String formatName,
|
||||
byte[] rawBytes,
|
||||
Integer orientation,
|
||||
String errorCorrectionLevel) {
|
||||
this.contents = contents;
|
||||
this.formatName = formatName;
|
||||
this.rawBytes = rawBytes;
|
||||
this.orientation = orientation;
|
||||
this.errorCorrectionLevel = errorCorrectionLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return raw content of barcode
|
||||
*/
|
||||
public String getContents() {
|
||||
return contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return name of format, like "QR_CODE", "UPC_A". See {@code BarcodeFormat} for more format names.
|
||||
*/
|
||||
public String getFormatName() {
|
||||
return formatName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return raw bytes of the barcode content, if applicable, or null otherwise
|
||||
*/
|
||||
public byte[] getRawBytes() {
|
||||
return rawBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return rotation of the image, in degrees, which resulted in a successful scan. May be null.
|
||||
*/
|
||||
public Integer getOrientation() {
|
||||
return orientation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return name of the error correction level used in the barcode, if applicable
|
||||
*/
|
||||
public String getErrorCorrectionLevel() {
|
||||
return errorCorrectionLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder dialogText = new StringBuilder(100);
|
||||
dialogText.append("Format: ").append(formatName).append('\n');
|
||||
dialogText.append("Contents: ").append(contents).append('\n');
|
||||
int rawBytesLength = rawBytes == null ? 0 : rawBytes.length;
|
||||
dialogText.append("Raw bytes: (").append(rawBytesLength).append(" bytes)\n");
|
||||
dialogText.append("Orientation: ").append(orientation).append('\n');
|
||||
dialogText.append("EC level: ").append(errorCorrectionLevel).append('\n');
|
||||
return dialogText.toString();
|
||||
}
|
||||
|
||||
}
|
||||
341
vendor/github.com/camlistore/camlistore/clients/android/src/org/camlistore/CamliActivity.java
generated
vendored
Normal file
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
Copyright 2011 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package org.camlistore;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.MessageQueue;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
public class CamliActivity extends Activity {
|
||||
private static final String TAG = "CamliActivity";
|
||||
private static final int MENU_SETTINGS = 1;
|
||||
private static final int MENU_STOP = 2;
|
||||
private static final int MENU_STOP_DIE = 3;
|
||||
private static final int MENU_UPLOAD_ALL = 4;
|
||||
private static final int MENU_VERSION = 5;
|
||||
|
||||
private IUploadService mServiceStub = null;
|
||||
private IStatusCallback mCallback = null;
|
||||
|
||||
// Status text update state, since it updates too quickly to do it the naive way.
|
||||
private long mLastStatusUpdate = 0; // time in millis we lasted updated the screen
|
||||
private String mStatusTextCurrent = null; // what the screen says
|
||||
private String mStatusTextWant = null; // what the service wants it to say
|
||||
|
||||
private final Handler mHandler = new Handler();
|
||||
|
||||
private final MessageQueue.IdleHandler mIdleHandler = new MessageQueue.IdleHandler() {
|
||||
@Override
|
||||
public boolean queueIdle() {
|
||||
if (mStatusTextCurrent != mStatusTextWant) {
|
||||
TextView textStats = (TextView) findViewById(R.id.textStats);
|
||||
mLastStatusUpdate = System.currentTimeMillis();
|
||||
mStatusTextCurrent = mStatusTextWant;
|
||||
textStats.setText(mStatusTextWant);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
private final ServiceConnection mServiceConnection = new ServiceConnection() {
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
mServiceStub = IUploadService.Stub.asInterface(service);
|
||||
Log.d(TAG, "Service connected, registering callback " + mCallback);
|
||||
|
||||
try {
|
||||
mServiceStub.registerCallback(mCallback);
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
Log.d(TAG, "Service disconnected");
|
||||
mServiceStub = null;
|
||||
};
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.main);
|
||||
|
||||
Looper.myQueue().addIdleHandler(mIdleHandler);
|
||||
final Button buttonToggle = (Button) findViewById(R.id.buttonToggle);
|
||||
|
||||
final TextView textStatus = (TextView) findViewById(R.id.textStatus);
|
||||
final TextView textStats = (TextView) findViewById(R.id.textStats);
|
||||
final TextView textBlobsRemain = (TextView) findViewById(R.id.textBlobsRemain);
|
||||
final TextView textUploadStatus = (TextView) findViewById(R.id.textUploadStatus);
|
||||
final ProgressBar progressBytes = (ProgressBar) findViewById(R.id.progressByteStatus);
|
||||
final ProgressBar progressFile = (ProgressBar) findViewById(R.id.progressFileStatus);
|
||||
|
||||
buttonToggle.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View btn) {
|
||||
Log.d(TAG, "button click! text=" + buttonToggle.getText());
|
||||
if (getString(R.string.pause).equals(buttonToggle.getText())) {
|
||||
try {
|
||||
Log.d(TAG, "Pausing..");
|
||||
mServiceStub.pause();
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
} else if (getString(R.string.resume).equals(buttonToggle.getText())) {
|
||||
try {
|
||||
Log.d(TAG, "Resuming..");
|
||||
mServiceStub.resume();
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mCallback = new IStatusCallback.Stub() {
|
||||
private volatile int mLastBlobsUploadRemain = 0;
|
||||
private volatile int mLastBlobsDigestRemain = 0;
|
||||
|
||||
@Override
|
||||
public void logToClient(String stuff) throws RemoteException {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUploading(final boolean uploading) throws RemoteException {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (uploading) {
|
||||
buttonToggle.setText(R.string.pause);
|
||||
textStatus.setText(R.string.uploading);
|
||||
} else if (mLastBlobsDigestRemain > 0) {
|
||||
buttonToggle.setText(R.string.pause);
|
||||
textStatus.setText(R.string.digesting);
|
||||
} else {
|
||||
buttonToggle.setText(R.string.resume);
|
||||
int stepsRemain = mLastBlobsUploadRemain + mLastBlobsDigestRemain;
|
||||
textStatus.setText(stepsRemain > 0 ? "Paused." : "Idle.");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFileStatus(final int done, final int inFlight, final int total) throws RemoteException {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
boolean finished = (done == total && mLastBlobsDigestRemain == 0);
|
||||
buttonToggle.setEnabled(!finished);
|
||||
progressFile.setMax(total);
|
||||
progressFile.setProgress(done);
|
||||
progressFile.setSecondaryProgress(done + inFlight);
|
||||
if (finished) {
|
||||
buttonToggle.setText(getString(R.string.pause_resume));
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder(40);
|
||||
sb.append("Files to upload: ").append(total - done);
|
||||
textBlobsRemain.setText(sb.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setByteStatus(final long done, final int inFlight, final long total) throws RemoteException {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// setMax takes an (signed) int, but 2GB is a totally
|
||||
// reasonable upload size, so use units of 1KB instead.
|
||||
progressBytes.setMax((int) (total / 1024L));
|
||||
progressBytes.setProgress((int) (done / 1024L));
|
||||
// TODO: renable once camput properly sends inflight information
|
||||
// progressBytes.setSecondaryProgress(progressBytes.getProgress() + inFlight / 1024);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUploadStatusText(final String text) throws RemoteException {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
textUploadStatus.setText(text);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUploadStatsText(final String text) throws RemoteException {
|
||||
// We were getting these status updates so quickly that the calls to TextView.setText
|
||||
// were consuming all CPU on the main thread and it was stalling the main thread
|
||||
// for seconds. Ridiculous. So instead, only update this every 5 milliseconds,
|
||||
// otherwise wait for the looper to be idle to update it.
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mStatusTextWant = text;
|
||||
long now = System.currentTimeMillis();
|
||||
if (mLastStatusUpdate < now - 5) {
|
||||
mStatusTextCurrent = mStatusTextWant;
|
||||
textStats.setText(mStatusTextWant);
|
||||
mLastStatusUpdate = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
// TODO: picking files/photos to upload?
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
// TODO Auto-generated method stub
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
super.onCreateOptionsMenu(menu);
|
||||
|
||||
MenuItem uploadAll = menu.add(Menu.NONE, MENU_UPLOAD_ALL, 0, R.string.upload_all);
|
||||
uploadAll.setIcon(android.R.drawable.ic_menu_upload);
|
||||
|
||||
MenuItem stop = menu.add(Menu.NONE, MENU_STOP, 0, R.string.stop);
|
||||
stop.setIcon(android.R.drawable.ic_menu_close_clear_cancel);
|
||||
|
||||
MenuItem stopDie = menu.add(Menu.NONE, MENU_STOP_DIE, 0, R.string.stop_die);
|
||||
stopDie.setIcon(android.R.drawable.ic_menu_close_clear_cancel);
|
||||
|
||||
MenuItem settings = menu.add(Menu.NONE, MENU_SETTINGS, 0, R.string.settings);
|
||||
settings.setIcon(android.R.drawable.ic_menu_preferences);
|
||||
|
||||
menu.add(Menu.NONE, MENU_VERSION, 0, R.string.version);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case MENU_STOP:
|
||||
try {
|
||||
if (mServiceStub != null) {
|
||||
mServiceStub.stopEverything();
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
// Ignore.
|
||||
}
|
||||
break;
|
||||
case MENU_STOP_DIE:
|
||||
System.exit(1);
|
||||
case MENU_SETTINGS:
|
||||
SettingsActivity.show(this);
|
||||
break;
|
||||
case MENU_VERSION:
|
||||
Toast.makeText(this, "camput version: " + ((UploadApplication) getApplication()).getCamputVersion(), Toast.LENGTH_LONG).show();
|
||||
break;
|
||||
case MENU_UPLOAD_ALL:
|
||||
Intent uploadAll = new Intent(UploadService.INTENT_UPLOAD_ALL);
|
||||
uploadAll.setClass(this, UploadService.class);
|
||||
Log.d(TAG, "Starting upload all...");
|
||||
startService(uploadAll);
|
||||
Log.d(TAG, "Back from upload all...");
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
try {
|
||||
if (mServiceStub != null)
|
||||
mServiceStub.unregisterCallback(mCallback);
|
||||
} catch (RemoteException e) {
|
||||
// Ignore.
|
||||
}
|
||||
if (mServiceConnection != null) {
|
||||
unbindService(mServiceConnection);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
SharedPreferences sp = getSharedPreferences(Preferences.NAME, 0);
|
||||
try {
|
||||
HostPort hp = new HostPort(sp.getString(Preferences.HOST, ""));
|
||||
if (!hp.isValid()) {
|
||||
// Crashes oddly in some Android Instrumentation thing if
|
||||
// uncommented:
|
||||
// SettingsActivity.show(this);
|
||||
// return;
|
||||
}
|
||||
} catch (NumberFormatException enf) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setMessage("Server should be of form [https://]<host[:port]>")
|
||||
.setTitle("Invalid Setting");
|
||||
AlertDialog alert = builder.create();
|
||||
alert.show();
|
||||
}
|
||||
|
||||
bindService(new Intent(this, UploadService.class), mServiceConnection, Context.BIND_AUTO_CREATE);
|
||||
|
||||
Intent intent = getIntent();
|
||||
String action = intent.getAction();
|
||||
Log.d(TAG, "onResume; action=" + action);
|
||||
|
||||
if (Intent.ACTION_SEND.equals(action) || Intent.ACTION_SEND_MULTIPLE.equals(action)) {
|
||||
Intent serviceIntent = new Intent(intent);
|
||||
serviceIntent.setClass(this, UploadService.class);
|
||||
startService(serviceIntent);
|
||||
setIntent(new Intent(this, CamliActivity.class));
|
||||
} else {
|
||||
Log.d(TAG, "Normal CamliActivity viewing.");
|
||||
}
|
||||
}
|
||||
}
|
||||
65
vendor/github.com/camlistore/camlistore/clients/android/src/org/camlistore/CamliFileObserver.java
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
Copyright 2011 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package org.camlistore;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.FileObserver;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import org.camlistore.IUploadService.Stub;
|
||||
|
||||
public class CamliFileObserver extends FileObserver {
|
||||
private static final String TAG = "CamliFileObserver";
|
||||
|
||||
private final File mDirectory;
|
||||
private final Stub mServiceStub;
|
||||
|
||||
public CamliFileObserver(IUploadService.Stub service, File directory) {
|
||||
super(directory.getAbsolutePath(), FileObserver.CLOSE_WRITE | FileObserver.MOVED_TO);
|
||||
// TODO: Docs say: "The monitored file or directory must exist at this
|
||||
// time, or else no events will be reported (even if it appears
|
||||
// later).". This means that a user without, say, a "gpx/" directory
|
||||
// that then goes to "Export all Tracks.." won't start them uploading.
|
||||
mDirectory = directory;
|
||||
mServiceStub = service;
|
||||
Log.d(TAG, "Starting to watch: " + mDirectory.getAbsolutePath());
|
||||
startWatching();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(int event, String path) {
|
||||
if (path == null) {
|
||||
// It's null for certain directory-level events.
|
||||
return;
|
||||
}
|
||||
|
||||
// Note from docs:
|
||||
// "This method is invoked on a special FileObserver thread."
|
||||
|
||||
// Order in which we get events for a new camera picture:
|
||||
// CREATE, OPEN, MODIFY, [OPEN, CLOSE_NOWRITE], CLOSE_WRITE
|
||||
File fullFile = new File(mDirectory, path);
|
||||
Log.d(TAG, "event " + event + " for " + fullFile.getAbsolutePath());
|
||||
try {
|
||||
mServiceStub.enqueueUpload(Uri.fromFile(fullFile));
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
56
vendor/github.com/camlistore/camlistore/clients/android/src/org/camlistore/DummyNullCallback.java
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
Copyright 2011 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package org.camlistore;
|
||||
|
||||
import android.os.RemoteException;
|
||||
|
||||
/**
|
||||
* No-op callback for service to use when it doesn't have a real callback.
|
||||
* Avoids a lot of null checks.
|
||||
*/
|
||||
public class DummyNullCallback extends IStatusCallback.Stub {
|
||||
|
||||
private static final IStatusCallback.Stub mInstance = new DummyNullCallback();
|
||||
|
||||
public static IStatusCallback.Stub instance() {
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logToClient(String stuff) throws RemoteException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setByteStatus(long done, int inFlight, long total) throws RemoteException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUploading(boolean uploading) throws RemoteException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUploadStatusText(String text) throws RemoteException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFileStatus(int done, int inFlight, int total) throws RemoteException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUploadStatsText(String text) throws RemoteException {
|
||||
}
|
||||
}
|
||||
103
vendor/github.com/camlistore/camlistore/clients/android/src/org/camlistore/HostPort.java
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
Copyright 2011 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package org.camlistore;
|
||||
|
||||
/**
|
||||
* HostPort parses a "host.com", "host.com:port", or "https://host.com:port"
|
||||
* It doesn't handle paths. TODO(bradfitz): This should probably be scrapped
|
||||
* and use a URL parser or something instead.
|
||||
*/
|
||||
public class HostPort {
|
||||
private final boolean mValid;
|
||||
private final String mHost;
|
||||
private final int mPort;
|
||||
private final boolean mSecure;
|
||||
|
||||
private static final String HTTP_PREFIX = "http://";
|
||||
private static final String SECURE_PREFIX = "https://";
|
||||
|
||||
public HostPort(String hostPort) {
|
||||
if (hostPort.startsWith(HTTP_PREFIX)) {
|
||||
mSecure = false;
|
||||
hostPort = hostPort.substring(HTTP_PREFIX.length());
|
||||
} else if (hostPort.startsWith(SECURE_PREFIX)) {
|
||||
mSecure = true;
|
||||
hostPort = hostPort.substring(SECURE_PREFIX.length());
|
||||
} else {
|
||||
mSecure = false;
|
||||
}
|
||||
|
||||
String[] parts = hostPort.split(":");
|
||||
if (parts.length == 2) {
|
||||
mHost = parts[0];
|
||||
mPort = new Integer(parts[1]).intValue();
|
||||
mValid = true;
|
||||
} else if (parts.length > 2 || parts.length == 0) {
|
||||
mValid = false;
|
||||
mHost = null;
|
||||
mPort = 0;
|
||||
} else {
|
||||
mValid = hostPort.length() > 0;
|
||||
mHost = hostPort;
|
||||
mPort = mSecure ? 443 : 80;
|
||||
}
|
||||
}
|
||||
|
||||
public int port() {
|
||||
return mPort;
|
||||
}
|
||||
|
||||
public String host() {
|
||||
return mHost;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return mValid;
|
||||
}
|
||||
|
||||
public boolean isSecure() {
|
||||
return mSecure;
|
||||
}
|
||||
|
||||
private boolean nonStandardPort() {
|
||||
return mPort != (mSecure ? 443 : 80);
|
||||
}
|
||||
|
||||
public String urlPrefix() {
|
||||
StringBuilder sb = new StringBuilder(12 + mHost.length());
|
||||
sb.append(httpScheme());
|
||||
sb.append("://");
|
||||
sb.append(mHost);
|
||||
if (nonStandardPort()) {
|
||||
sb.append(":");
|
||||
sb.append(mPort);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String httpScheme() {
|
||||
return mSecure ? "https" : "http";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (!mValid) {
|
||||
return "[invalid HostPort]";
|
||||
}
|
||||
return mHost + ":" + mPort;
|
||||
}
|
||||
}
|
||||
30
vendor/github.com/camlistore/camlistore/clients/android/src/org/camlistore/IStatusCallback.aidl
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Copyright 2011 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package org.camlistore;
|
||||
|
||||
oneway interface IStatusCallback {
|
||||
void logToClient(String stuff);
|
||||
void setUploadStatusText(String text); // single line
|
||||
void setUploadStatsText(String text); // big box
|
||||
void setUploading(boolean uploading);
|
||||
|
||||
// done: acknowledged by server
|
||||
// inFlight: those written to the server, but no reply yet (i.e. large HTTP POST body) (does NOT include the "done" ones)
|
||||
// total: "this batch" size. reset on transition from 0 -> 1 blobs remain.
|
||||
void setFileStatus(int done, int inFlight, int total);
|
||||
void setByteStatus(long done, int inFlight, long total);
|
||||
}
|
||||
49
vendor/github.com/camlistore/camlistore/clients/android/src/org/camlistore/IUploadService.aidl
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
Copyright 2011 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package org.camlistore;
|
||||
|
||||
import org.camlistore.IStatusCallback;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.net.Uri;
|
||||
import java.util.List;
|
||||
|
||||
interface IUploadService {
|
||||
void registerCallback(IStatusCallback cb);
|
||||
void unregisterCallback(IStatusCallback cb);
|
||||
|
||||
int queueSize();
|
||||
boolean isUploading();
|
||||
|
||||
// Returns true if thread was running and we requested it be stopped.
|
||||
boolean pause();
|
||||
|
||||
// Returns true if upload wasn't already in progress and new upload
|
||||
// thread was started.
|
||||
boolean resume();
|
||||
|
||||
// Enqueues a new file to be uploaded (a file:// or content:// URI). Does disk I/O,
|
||||
// so should be called from an AsyncTask.
|
||||
// Returns false if server not configured.
|
||||
boolean enqueueUpload(in Uri uri);
|
||||
int enqueueUploadList(in List<Uri> uri);
|
||||
|
||||
// Stop stop uploads, clear queues.
|
||||
void stopEverything();
|
||||
|
||||
// For the SettingsActivity
|
||||
void setBackgroundWatchersEnabled(boolean enabled);
|
||||
}
|
||||
32
vendor/github.com/camlistore/camlistore/clients/android/src/org/camlistore/OnAlarmReceiver.java
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
Copyright 2011 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package org.camlistore;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
|
||||
public class OnAlarmReceiver extends BroadcastReceiver {
|
||||
private static final String TAG = "Camli_OnAlarmReceiver";
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Log.d(TAG, "alarm");
|
||||
}
|
||||
|
||||
}
|
||||
43
vendor/github.com/camlistore/camlistore/clients/android/src/org/camlistore/OnBootReceiver.java
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Copyright 2011 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package org.camlistore;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
|
||||
public class OnBootReceiver extends BroadcastReceiver {
|
||||
private static final String TAG = "Camli_OnBootReceiver";
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Log.v(TAG, "onReceive on boot");
|
||||
AlarmManager alarmer = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, new Intent(context,
|
||||
OnAlarmReceiver.class), 0);
|
||||
|
||||
alarmer.setInexactRepeating(AlarmManager.ELAPSED_REALTIME,
|
||||
SystemClock.elapsedRealtime() + 60000, AlarmManager.INTERVAL_HALF_HOUR,
|
||||
pendingIntent);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
114
vendor/github.com/camlistore/camlistore/clients/android/src/org/camlistore/Preferences.java
generated
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
Copyright 2011 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package org.camlistore;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
public final class Preferences {
|
||||
public static final String NAME = "CamliUploader";
|
||||
|
||||
public static final String HOST = "camli.host";
|
||||
// TODO(mpl): list instead of single string later? seems overkill for now.
|
||||
public static final String TRUSTED_CERT = "camli.trusted_cert";
|
||||
public static final String USERNAME = "camli.username";
|
||||
public static final String PASSWORD = "camli.password";
|
||||
public static final String AUTO = "camli.auto";
|
||||
public static final String AUTO_OPTS = "camli.auto.opts";
|
||||
public static final String MAX_CACHE_MB = "camli.max_cache_mb";
|
||||
public static final String DEV_IP = "camli.dev_ip";
|
||||
public static final String AUTO_REQUIRE_POWER = "camli.auto.require_power";
|
||||
public static final String AUTO_REQUIRE_WIFI = "camli.auto.require_wifi";
|
||||
public static final String AUTO_REQUIRED_WIFI_SSID = "camli.auto.required_wifi_ssid";
|
||||
public static final String AUTO_DIR_PHOTOS = "camli.auto.photos";
|
||||
public static final String AUTO_DIR_MYTRACKS = "camli.auto.mytracks";
|
||||
|
||||
private final SharedPreferences mSP;
|
||||
|
||||
public Preferences(SharedPreferences prefs) {
|
||||
mSP = prefs;
|
||||
}
|
||||
|
||||
public boolean autoRequiresPower() {
|
||||
return mSP.getBoolean(AUTO_REQUIRE_POWER, false);
|
||||
}
|
||||
|
||||
public boolean autoRequiresWifi() {
|
||||
return mSP.getBoolean(AUTO_REQUIRE_WIFI, false);
|
||||
}
|
||||
|
||||
public String autoRequiredWifiSSID() {
|
||||
return mSP.getString(AUTO_REQUIRED_WIFI_SSID, "");
|
||||
}
|
||||
|
||||
public boolean autoUpload() {
|
||||
return mSP.getBoolean(AUTO, false);
|
||||
}
|
||||
|
||||
public int maxCacheMb() {
|
||||
return Integer.parseInt(mSP.getString(MAX_CACHE_MB, "256"));
|
||||
}
|
||||
|
||||
public long maxCacheBytes() {
|
||||
return maxCacheMb() * 1024 * 1024;
|
||||
}
|
||||
|
||||
public boolean autoDirPhotos() {
|
||||
return mSP.getBoolean(AUTO_DIR_PHOTOS, true);
|
||||
}
|
||||
|
||||
public boolean autoDirMyTracks() {
|
||||
return mSP.getBoolean(AUTO_DIR_MYTRACKS, true);
|
||||
}
|
||||
|
||||
private String devIP() {
|
||||
return mSP.getString(DEV_IP, "");
|
||||
}
|
||||
|
||||
private boolean inDevMode() {
|
||||
return !devIP().isEmpty();
|
||||
}
|
||||
|
||||
public String username() {
|
||||
if (inDevMode()) {
|
||||
return "camlistore";
|
||||
}
|
||||
return mSP.getString(USERNAME, "");
|
||||
}
|
||||
|
||||
public String password() {
|
||||
if (inDevMode()) {
|
||||
return "pass3179";
|
||||
}
|
||||
return mSP.getString(PASSWORD, "");
|
||||
}
|
||||
|
||||
public HostPort hostPort() {
|
||||
if (inDevMode()) {
|
||||
return new HostPort("http://" + devIP() + ":3179");
|
||||
}
|
||||
return new HostPort(mSP.getString(Preferences.HOST, ""));
|
||||
}
|
||||
|
||||
public String trustedCert() {
|
||||
return mSP.getString(TRUSTED_CERT, "").toLowerCase();
|
||||
}
|
||||
|
||||
public void setDevIP(String value) {
|
||||
mSP.edit().putString(DEV_IP, value).apply();
|
||||
}
|
||||
|
||||
}
|
||||
31
vendor/github.com/camlistore/camlistore/clients/android/src/org/camlistore/QRPreference.java
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
package org.camlistore;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.preference.Preference;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.zxing.integration.android.IntentIntegrator;
|
||||
|
||||
/**
|
||||
* QRPrefence implements a custom {@link Preference} for scanning barcodes.
|
||||
*
|
||||
* It will launch a barcode scanner intent configured for scanning QR codes using {@link IntentIntegrator}. If no barcode scanner app is installed {@link IntentIntegrator} will prompt the user to install one from the Google Play market.
|
||||
*/
|
||||
public class QRPreference extends Preference {
|
||||
private static final String TAG = "QRPreference";
|
||||
|
||||
public QRPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
Log.v(TAG, "QRPreference");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onClick() {
|
||||
SettingsActivity activity = (SettingsActivity) this.getContext();
|
||||
IntentIntegrator integrator = new IntentIntegrator(activity);
|
||||
integrator.initiateScan(IntentIntegrator.QR_CODE_TYPES);
|
||||
}
|
||||
}
|
||||
84
vendor/github.com/camlistore/camlistore/clients/android/src/org/camlistore/QueuedFile.java
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
Copyright 2011 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package org.camlistore;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
/**
|
||||
* Immutable struct for tuple (sha1 blobRef, URI to upload, size of blob).
|
||||
*/
|
||||
public class QueuedFile {
|
||||
|
||||
private final Uri mUri;
|
||||
private final long mSize;
|
||||
private final String mDiskPath; // or null if it can't be resolved.
|
||||
|
||||
public QueuedFile(Uri uri, long size, String diskPath) {
|
||||
if (uri == null) {
|
||||
throw new NullPointerException("uri == null");
|
||||
}
|
||||
mUri = uri;
|
||||
mSize = size;
|
||||
mDiskPath = diskPath;
|
||||
}
|
||||
|
||||
public Uri getUri() {
|
||||
return mUri;
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return mSize;
|
||||
}
|
||||
|
||||
// getDiskPath may return null, if the URI couldn't be resolved to a path on disk.
|
||||
public String getDiskPath() {
|
||||
return mDiskPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "QueuedFile [mSize=" + mSize + ", mUri=" + mUri + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + (int) (mSize ^ (mSize >>> 32));
|
||||
result = prime * result + ((mUri == null) ? 0 : mUri.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
QueuedFile other = (QueuedFile) obj;
|
||||
if (mSize != other.mSize)
|
||||
return false;
|
||||
if (mUri == null) {
|
||||
if (other.mUri != null)
|
||||
return false;
|
||||
} else if (!mUri.equals(other.mUri))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
355
vendor/github.com/camlistore/camlistore/clients/android/src/org/camlistore/SettingsActivity.java
generated
vendored
Normal file
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
Copyright 2011 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package org.camlistore;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.net.wifi.WifiInfo;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.preference.CheckBoxPreference;
|
||||
import android.preference.EditTextPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceChangeListener;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.preference.PreferenceScreen;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.zxing.integration.android.IntentIntegrator;
|
||||
import com.google.zxing.integration.android.IntentResult;
|
||||
|
||||
public class SettingsActivity extends PreferenceActivity {
|
||||
private static final String TAG = "SettingsActivity";
|
||||
|
||||
private IUploadService mServiceStub = null;
|
||||
|
||||
private EditTextPreference hostPref;
|
||||
private EditTextPreference trustedCertPref;
|
||||
private EditTextPreference usernamePref;
|
||||
private EditTextPreference passwordPref;
|
||||
private EditTextPreference devIPPref;
|
||||
private CheckBoxPreference autoPref;
|
||||
private PreferenceScreen autoOpts;
|
||||
private EditTextPreference maxCacheSizePref;
|
||||
|
||||
private SharedPreferences mSharedPrefs;
|
||||
private Preferences mPrefs;
|
||||
|
||||
private Map<CharSequence, String> prefToParam;
|
||||
|
||||
private final ServiceConnection mServiceConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
mServiceStub = IUploadService.Stub.asInterface(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
mServiceStub = null;
|
||||
};
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Map<CharSequence, String> m = new HashMap<CharSequence, String>();
|
||||
m.put(Preferences.HOST, "server");
|
||||
m.put(Preferences.TRUSTED_CERT, "certFingerprint");
|
||||
m.put(Preferences.USERNAME, "username");
|
||||
m.put(Preferences.PASSWORD, "password");
|
||||
m.put(Preferences.AUTO, "autoUpload");
|
||||
m.put(Preferences.MAX_CACHE_MB, "maxCacheSize");
|
||||
prefToParam = Collections.unmodifiableMap(m);
|
||||
|
||||
getPreferenceManager().setSharedPreferencesName(Preferences.NAME);
|
||||
addPreferencesFromResource(R.xml.preferences);
|
||||
|
||||
hostPref = (EditTextPreference) findPreference(Preferences.HOST);
|
||||
// TODO(mpl): popup window that proposes to automatically add the cert to
|
||||
// the prefs when we fail to dial an untrusted server (and only in that case).
|
||||
trustedCertPref = (EditTextPreference) findPreference(Preferences.TRUSTED_CERT);
|
||||
usernamePref = (EditTextPreference) findPreference(Preferences.USERNAME);
|
||||
passwordPref = (EditTextPreference) findPreference(Preferences.PASSWORD);
|
||||
autoPref = (CheckBoxPreference) findPreference(Preferences.AUTO);
|
||||
autoOpts = (PreferenceScreen) findPreference(Preferences.AUTO_OPTS);
|
||||
maxCacheSizePref = (EditTextPreference) findPreference(Preferences.MAX_CACHE_MB);
|
||||
devIPPref = (EditTextPreference) findPreference(Preferences.DEV_IP);
|
||||
|
||||
mSharedPrefs = getSharedPreferences(Preferences.NAME, 0);
|
||||
mPrefs = new Preferences(mSharedPrefs);
|
||||
|
||||
// Display defaults.
|
||||
maxCacheSizePref.setSummary(getString(
|
||||
R.string.settings_max_cache_size_summary, mPrefs.maxCacheMb()));
|
||||
|
||||
OnPreferenceChangeListener onChange = new OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference pref, Object newValue) {
|
||||
final String key = pref.getKey();
|
||||
Log.v(TAG, "preference change for: " + key);
|
||||
|
||||
// Note: newValue isn't yet persisted, but easiest to update the
|
||||
// UI here.
|
||||
String newStr = (newValue instanceof String) ? (String) newValue
|
||||
: null;
|
||||
if (pref == hostPref) {
|
||||
updateHostSummary(newStr);
|
||||
} else if (pref == trustedCertPref) {
|
||||
updateTrustedCertSummary(newStr);
|
||||
} else if (pref == passwordPref) {
|
||||
updatePasswordSummary(newStr);
|
||||
} else if (pref == usernamePref) {
|
||||
updateUsernameSummary(newStr);
|
||||
} else if (pref == maxCacheSizePref) {
|
||||
if (!updateMaxCacheSizeSummary(newStr))
|
||||
return false;
|
||||
} else if (pref == devIPPref) {
|
||||
updateDevIP(newStr);
|
||||
}
|
||||
return true; // yes, persist it
|
||||
}
|
||||
};
|
||||
hostPref.setOnPreferenceChangeListener(onChange);
|
||||
trustedCertPref.setOnPreferenceChangeListener(onChange);
|
||||
passwordPref.setOnPreferenceChangeListener(onChange);
|
||||
usernamePref.setOnPreferenceChangeListener(onChange);
|
||||
maxCacheSizePref.setOnPreferenceChangeListener(onChange);
|
||||
devIPPref.setOnPreferenceChangeListener(onChange);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives the results from the custome QRPreference's call to the barcode scanner intent.
|
||||
*
|
||||
* This is never called if the user doesn't have a zxing barcode scanner app installed.
|
||||
*/
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
|
||||
if (scanResult != null && scanResult.getContents() != null) {
|
||||
// handle scan result
|
||||
Log.v(TAG, "Scan result" + scanResult);
|
||||
Uri uri = Uri.parse(scanResult.getContents());
|
||||
confirmNewSettingsDialog(uri);
|
||||
} else {
|
||||
// else continue with any other code you need in the method
|
||||
Log.v(TAG, "No result");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* confirmNewSettingsDialog will set preferences based on the parameters
|
||||
* in uri.
|
||||
*
|
||||
* It is expected the schema of uri is 'camli' and the host is 'settings'.
|
||||
* Uri parameters expected are server, certFingerprint, username,
|
||||
* autoUpload, maxCacheSize, and password
|
||||
*/
|
||||
private final void confirmNewSettingsDialog(final Uri uri) {
|
||||
Log.v(TAG, "QR resolved to: " + uri);
|
||||
if (!(uri.getScheme().equals("camli") && uri.getHost().equals("settings"))) {
|
||||
Toast.makeText(this, "QR code not a camli://settings/ URL", Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
StringBuilder confirmation = new StringBuilder();
|
||||
|
||||
for (Map.Entry<CharSequence, String> pref : prefToParam.entrySet()) {
|
||||
confirmation.append(findPreference(pref.getKey()).getTitle());
|
||||
confirmation.append(": ");
|
||||
confirmation.append(uri.getQueryParameter(pref.getValue()));
|
||||
confirmation.append("\n");
|
||||
}
|
||||
|
||||
builder.setMessage(confirmation.toString())
|
||||
.setTitle(R.string.settings_confirmation_dialog_title);
|
||||
|
||||
builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
hostPref.setText(uri.getQueryParameter("server"));
|
||||
trustedCertPref.setText(uri.getQueryParameter("certFingerprint"));
|
||||
usernamePref.setText(uri.getQueryParameter("username"));
|
||||
String auto = uri.getQueryParameter("autoUpload");
|
||||
autoPref.setChecked(auto != null && auto.equals("1"));
|
||||
maxCacheSizePref.setText(uri.getQueryParameter("maxCacheSize"));
|
||||
// Password isn't a value that can be set on /ui/mobile.html. It
|
||||
// seems like a security risk to do so. If there's a smart way to do it,
|
||||
// I'm up for suggestions. In the meantime, if a person manually
|
||||
// adds it to the QR code URL, use it (helpful during development)
|
||||
// -wathiede.
|
||||
passwordPref.setText(uri.getQueryParameter("password"));
|
||||
|
||||
updatePreferenceSummaries();
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(R.string.cancel, null);
|
||||
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private final SharedPreferences.OnSharedPreferenceChangeListener prefChangedHandler = new SharedPreferences.OnSharedPreferenceChangeListener() {
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sp, String key) {
|
||||
if (Preferences.AUTO.equals(key)) {
|
||||
boolean val = mPrefs.autoUpload();
|
||||
updateAutoOpts(val);
|
||||
Log.d(TAG, "AUTO changed to " + val);
|
||||
if (mServiceStub != null) {
|
||||
try {
|
||||
mServiceStub.setBackgroundWatchersEnabled(val);
|
||||
} catch (RemoteException e) {
|
||||
// Ignore.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
mSharedPrefs
|
||||
.unregisterOnSharedPreferenceChangeListener(prefChangedHandler);
|
||||
if (mServiceConnection != null) {
|
||||
unbindService(mServiceConnection);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
updatePreferenceSummaries();
|
||||
mSharedPrefs
|
||||
.registerOnSharedPreferenceChangeListener(prefChangedHandler);
|
||||
bindService(new Intent(this, UploadService.class), mServiceConnection,
|
||||
Context.BIND_AUTO_CREATE);
|
||||
}
|
||||
|
||||
private void updatePreferenceSummaries() {
|
||||
updateHostSummary(hostPref.getText());
|
||||
updateTrustedCertSummary(trustedCertPref.getText());
|
||||
updatePasswordSummary(passwordPref.getText());
|
||||
updateAutoOpts(autoPref.isChecked());
|
||||
updateMaxCacheSizeSummary(maxCacheSizePref.getText());
|
||||
updateUsernameSummary(usernamePref.getText());
|
||||
updateDevIP(devIPPref.getText());
|
||||
}
|
||||
|
||||
private void updateDevIP(String value) {
|
||||
// The Brad-is-lazy shortcut: if the user enters "12", assumes
|
||||
// "10.0.0.12", or whatever
|
||||
// the current wifi connections's /24 is.
|
||||
if (!TextUtils.isEmpty(value) && !value.contains(".")) {
|
||||
WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
|
||||
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
|
||||
if (wifiInfo != null) {
|
||||
int ip = wifiInfo.getIpAddress();
|
||||
value = String.format("%d.%d.%d.", ip & 0xff, (ip >> 8) & 0xff,
|
||||
(ip >> 16) & 0xff) + value;
|
||||
devIPPref.setText(value);
|
||||
mPrefs.setDevIP(value);
|
||||
}
|
||||
|
||||
}
|
||||
boolean enabled = TextUtils.isEmpty(value);
|
||||
hostPref.setEnabled(enabled);
|
||||
trustedCertPref.setEnabled(enabled);
|
||||
usernamePref.setEnabled(enabled);
|
||||
passwordPref.setEnabled(enabled);
|
||||
if (!enabled) {
|
||||
devIPPref.setSummary("Using http://" + value
|
||||
+ ":3179 user/pass \"camlistore\", \"pass3179\"");
|
||||
} else {
|
||||
devIPPref.setSummary("(Dev-server IP to override settings above)");
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePasswordSummary(String value) {
|
||||
if (value != null && value.length() > 0) {
|
||||
passwordPref.setSummary("*********");
|
||||
} else {
|
||||
passwordPref.setSummary("<unset>");
|
||||
}
|
||||
}
|
||||
|
||||
private void updateUsernameSummary(String value) {
|
||||
if (value != null && value.length() > 0) {
|
||||
usernamePref.setSummary(value);
|
||||
} else {
|
||||
usernamePref.setSummary("<unset>");
|
||||
}
|
||||
}
|
||||
|
||||
private void updateHostSummary(String value) {
|
||||
if (value != null && value.length() > 0) {
|
||||
hostPref.setSummary(value);
|
||||
} else {
|
||||
hostPref.setSummary(getString(R.string.settings_host_summary));
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTrustedCertSummary(String value) {
|
||||
if (value != null && value.length() > 0) {
|
||||
trustedCertPref.setSummary(value);
|
||||
} else {
|
||||
trustedCertPref.setSummary("<unset; optional 20 hex SHA-256 prefix>");
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAutoOpts(boolean checked) {
|
||||
autoOpts.setEnabled(checked);
|
||||
}
|
||||
|
||||
// Update the summary for the max cache size setting.
|
||||
// Returns true if the value is valid and should be persisted and false
|
||||
// otherwise.
|
||||
private boolean updateMaxCacheSizeSummary(String value) {
|
||||
try {
|
||||
int mb = Integer.parseInt(value);
|
||||
if (mb <= 0)
|
||||
return false;
|
||||
maxCacheSizePref.setSummary(getString(
|
||||
R.string.settings_max_cache_size_summary, mb));
|
||||
return true;
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience method.
|
||||
static void show(Context context) {
|
||||
final Intent intent = new Intent(context, SettingsActivity.class);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
}
|
||||
128
vendor/github.com/camlistore/camlistore/clients/android/src/org/camlistore/UploadApplication.java
generated
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
Copyright 2011 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package org.camlistore;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.util.Log;
|
||||
|
||||
public class UploadApplication extends Application {
|
||||
private final static String TAG = "UploadApplication";
|
||||
private final static boolean STRICT_MODE = true;
|
||||
|
||||
private long getAPKModTime() {
|
||||
try {
|
||||
return getPackageManager().getPackageInfo(getPackageName(), 0).lastUpdateTime;
|
||||
} catch (NameNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyGoBinary() {
|
||||
long myTime = getAPKModTime();
|
||||
String dstFile = getBaseContext().getFilesDir().getAbsolutePath() + "/camput.bin";
|
||||
File f = new File(dstFile);
|
||||
Log.d(TAG, " My Time: " + myTime);
|
||||
Log.d(TAG, "Bin Time: " + f.lastModified());
|
||||
if (f.exists() && f.lastModified() > myTime) {
|
||||
Log.d(TAG, "Go binary modtime up-to-date.");
|
||||
return;
|
||||
}
|
||||
Log.d(TAG, "Go binary missing or modtime stale. Re-copying from APK.");
|
||||
try {
|
||||
InputStream is = getAssets().open("camput.arm");
|
||||
FileOutputStream fos = getBaseContext().openFileOutput("camput.bin.writing", MODE_PRIVATE);
|
||||
byte[] buf = new byte[8192];
|
||||
int offset;
|
||||
while ((offset = is.read(buf)) > 0) {
|
||||
fos.write(buf, 0, offset);
|
||||
}
|
||||
is.close();
|
||||
fos.flush();
|
||||
fos.close();
|
||||
|
||||
String writingFilePath = dstFile + ".writing";
|
||||
Log.d(TAG, "wrote out " + writingFilePath);
|
||||
Runtime.getRuntime().exec("chmod 0777 " + writingFilePath);
|
||||
Log.d(TAG, "did chmod 0700 on " + writingFilePath);
|
||||
Runtime.getRuntime().exec("mv " + writingFilePath + " " + dstFile);
|
||||
f = new File(dstFile);
|
||||
f.setLastModified(myTime);
|
||||
Log.d(TAG, "set modtime of " + dstFile);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
copyGoBinary();
|
||||
|
||||
if (!STRICT_MODE) {
|
||||
Log.d(TAG, "Starting UploadApplication; release build.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Runtime.getRuntime().exec("chmod 0755 " + getCacheDir().getAbsolutePath());
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "failed to chmod cache dir");
|
||||
}
|
||||
|
||||
try {
|
||||
Class strictmode = Class.forName("android.os.StrictMode");
|
||||
Log.d(TAG, "StrictMode class found.");
|
||||
Method method = strictmode.getMethod("enableDefaults");
|
||||
Log.d(TAG, "enableDefaults method found.");
|
||||
method.invoke(null);
|
||||
} catch (ClassNotFoundException e) {
|
||||
} catch (LinkageError e) {
|
||||
} catch (IllegalAccessException e) {
|
||||
} catch (NoSuchMethodException e) {
|
||||
} catch (SecurityException e) {
|
||||
} catch (java.lang.reflect.InvocationTargetException e) {
|
||||
}
|
||||
}
|
||||
|
||||
public String getCamputVersion() {
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = getAssets().open("camput-version.txt");
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
|
||||
return br.readLine();
|
||||
} catch (IOException e) {
|
||||
return e.toString();
|
||||
} finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
846
vendor/github.com/camlistore/camlistore/clients/android/src/org/camlistore/UploadService.java
generated
vendored
Normal file
@@ -0,0 +1,846 @@
|
||||
/*
|
||||
Copyright 2011 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package org.camlistore;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.camlistore.UploadThread.CamputChunkUploadedMessage;
|
||||
import org.camlistore.UploadThread.CamputStatsMessage;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.Notification.Builder;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.os.FileObserver;
|
||||
import android.os.IBinder;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.Parcelable;
|
||||
import android.os.PowerManager;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
public class UploadService extends Service {
|
||||
private static final String TAG = "UploadService";
|
||||
|
||||
private static int NOTIFY_ID_UPLOADING = 0x001;
|
||||
|
||||
public static final String INTENT_POWER_CONNECTED = "POWER_CONNECTED";
|
||||
public static final String INTENT_POWER_DISCONNECTED = "POWER_DISCONNECTED";
|
||||
public static final String INTENT_UPLOAD_ALL = "UPLOAD_ALL";
|
||||
public static final String INTENT_NETWORK_WIFI = "WIFI_NOW";
|
||||
public static final String INTENT_NETWORK_NOT_WIFI = "NOT_WIFI_NOW";
|
||||
|
||||
// Everything in this block guarded by 'this':
|
||||
private boolean mUploading = false; // user's desired state (notified
|
||||
// quickly)
|
||||
private UploadThread mUploadThread = null; // last thread created; null when
|
||||
// thread exits
|
||||
private Notification.Builder mNotificationBuilder; // null until upload is
|
||||
// started/resumed
|
||||
private final Map<QueuedFile, Long> mFileBytesRemain = new HashMap<QueuedFile, Long>();
|
||||
private final LinkedList<QueuedFile> mQueueList = new LinkedList<QueuedFile>();
|
||||
private final Map<String, Long> mStatValue = new TreeMap<String, Long>();
|
||||
private IStatusCallback mCallback = DummyNullCallback.instance();
|
||||
private String mLastUploadStatusText = null; // single line
|
||||
private String mLastUploadStatsText = null; // multi-line stats
|
||||
private int mBytesInFlight = 0;
|
||||
private int mFilesInFlight = 0;
|
||||
|
||||
// Stats, all guarded by 'this', and all reset to 0 on queue size transition
|
||||
// from 0 -> 1.
|
||||
private long mBytesTotal = 0;
|
||||
private long mBytesUploaded = 0;
|
||||
private int mFilesTotal = 0;
|
||||
private int mFilesUploaded = 0;
|
||||
|
||||
// Effectively final, initialized in onCreate():
|
||||
PowerManager mPowerManager;
|
||||
WifiManager mWifiManager;
|
||||
NotificationManager mNotificationManager;
|
||||
Preferences mPrefs;
|
||||
|
||||
// File Observers. Need to keep a reference to them, as there's no JNI
|
||||
// reference and their finalizers would run otherwise, stopping their
|
||||
// inotify.
|
||||
private final ArrayList<FileObserver> mObservers = new ArrayList<FileObserver>();
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
Log.d(TAG, "onCreate");
|
||||
|
||||
mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||
mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
|
||||
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
||||
mPrefs = new Preferences(getSharedPreferences(Preferences.NAME, 0));
|
||||
|
||||
updateBackgroundWatchers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
Log.d(TAG, "onBind intent=" + intent);
|
||||
return service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(Intent intent, int startId) {
|
||||
handleCommand(intent);
|
||||
}
|
||||
|
||||
private void startUploadService() {
|
||||
startService(new Intent(UploadService.this, UploadService.class));
|
||||
}
|
||||
|
||||
// This is @Override as of SDK version 5, but we're targetting 4 (Android
|
||||
// 1.6)
|
||||
private static final int START_STICKY = 1; // in SDK 5
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
handleCommand(intent);
|
||||
// We want this service to continue running until it is explicitly
|
||||
// stopped, so return sticky.
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
private void handleCommand(Intent intent) {
|
||||
Log.d(TAG, "in handleCommand() for onStart() intent: " + intent);
|
||||
if (intent == null) {
|
||||
stopServiceIfEmpty();
|
||||
return;
|
||||
}
|
||||
|
||||
String action = intent.getAction();
|
||||
if (Intent.ACTION_SEND.equals(action)) {
|
||||
handleSend(intent);
|
||||
stopServiceIfEmpty();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Intent.ACTION_SEND_MULTIPLE.equals(action)) {
|
||||
handleSendMultiple(intent);
|
||||
stopServiceIfEmpty();
|
||||
return;
|
||||
}
|
||||
|
||||
if (INTENT_UPLOAD_ALL.equals(action)) {
|
||||
handleUploadAll();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (mPrefs.autoUpload()) {
|
||||
boolean startAuto = false;
|
||||
boolean stopAuto = false;
|
||||
|
||||
if (INTENT_POWER_CONNECTED.equals(action)) {
|
||||
if (!mPrefs.autoRequiresWifi() || WifiPowerReceiver.onWifi(this)) {
|
||||
startAuto = true;
|
||||
}
|
||||
} else if (INTENT_NETWORK_WIFI.equals(action)) {
|
||||
if (!mPrefs.autoRequiresPower() || WifiPowerReceiver.onPower(this)) {
|
||||
String ssid = "";
|
||||
String requiredSSID = mPrefs.autoRequiredWifiSSID();
|
||||
if (intent.hasExtra("SSID")) {
|
||||
ssid = intent.getStringExtra("SSID");
|
||||
}
|
||||
Log.d(TAG, "SSID: '" + ssid +"' / Required SSID: '" + requiredSSID + "'");
|
||||
if (requiredSSID.equals("") || requiredSSID.equals(ssid)) {
|
||||
startAuto = true;
|
||||
}
|
||||
}
|
||||
} else if (INTENT_POWER_DISCONNECTED.equals(action)) {
|
||||
stopAuto = mPrefs.autoRequiresPower();
|
||||
} else if (INTENT_NETWORK_NOT_WIFI.equals(action)) {
|
||||
stopAuto = mPrefs.autoRequiresWifi();
|
||||
}
|
||||
|
||||
if (startAuto) {
|
||||
Log.d(TAG, "Starting automatic uploads");
|
||||
service.resume();
|
||||
handleUploadAll();
|
||||
return;
|
||||
}
|
||||
if (stopAuto) {
|
||||
Log.d(TAG, "Stopping automatic uploads");
|
||||
service.pause();
|
||||
stopBackgroundWatchers();
|
||||
stopServiceIfEmpty();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
// Ignore.
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSend(Intent intent) {
|
||||
Bundle extras = intent.getExtras();
|
||||
if (extras == null) {
|
||||
Log.w(TAG, "expected extras in handleSend");
|
||||
return;
|
||||
}
|
||||
|
||||
extras.keySet(); // unparcel
|
||||
Log.d(TAG, "handleSend; extras=" + extras);
|
||||
|
||||
Object streamValue = extras.get("android.intent.extra.STREAM");
|
||||
if (!(streamValue instanceof Uri)) {
|
||||
Log.w(TAG, "Expected URI for STREAM; got: " + streamValue);
|
||||
return;
|
||||
}
|
||||
|
||||
final Uri uri = (Uri) streamValue;
|
||||
Util.runAsync(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
service.enqueueUpload(uri);
|
||||
} catch (RemoteException e) {
|
||||
} finally {
|
||||
stopServiceIfEmpty();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleUploadAll() {
|
||||
startService(new Intent(UploadService.this, UploadService.class));
|
||||
final PowerManager.WakeLock wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Camli Upload All");
|
||||
wakeLock.acquire();
|
||||
Util.runAsync(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
List<String> dirs = getBackupDirs();
|
||||
List<Uri> filesToQueue = new ArrayList<Uri>();
|
||||
for (String dirName : dirs) {
|
||||
File dir = new File(dirName);
|
||||
if (!dir.exists()) {
|
||||
continue;
|
||||
}
|
||||
File[] files = dir.listFiles();
|
||||
if (files != null) {
|
||||
for (int i = 0; i < files.length; ++i) {
|
||||
File f = files[i];
|
||||
if (f.isDirectory()) {
|
||||
// Skip thumbnails directory.
|
||||
// TODO: are any interesting enough to recurse into?
|
||||
// Definitely don't need to upload thumbnails, but
|
||||
// but maybe some other app in the the future creates
|
||||
// sharded directories. Eye-Fi doesn't, though.
|
||||
continue;
|
||||
}
|
||||
filesToQueue.add(Uri.fromFile(f));
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
service.enqueueUploadList(filesToQueue);
|
||||
} catch (RemoteException e) {
|
||||
} finally {
|
||||
stopServiceIfEmpty();
|
||||
}
|
||||
} finally {
|
||||
wakeLock.release();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private List<String> getBackupDirs() {
|
||||
ArrayList<String> dirs = new ArrayList<String>();
|
||||
if (mPrefs.autoDirPhotos()) {
|
||||
dirs.add(Environment.getExternalStorageDirectory() + "/DCIM/Camera");
|
||||
dirs.add(Environment.getExternalStorageDirectory() + "/DCIM/100MEDIA");
|
||||
dirs.add(Environment.getExternalStorageDirectory() + "/DCIM/100ANDRO");
|
||||
dirs.add(Environment.getExternalStorageDirectory() + "/Eye-Fi");
|
||||
}
|
||||
if (mPrefs.autoDirMyTracks()) {
|
||||
dirs.add(Environment.getExternalStorageDirectory() + "/gpx");
|
||||
dirs.add(Environment.getExternalStorageDirectory() + "/kml");
|
||||
}
|
||||
return dirs;
|
||||
}
|
||||
|
||||
private void handleSendMultiple(Intent intent) {
|
||||
ArrayList<Parcelable> items = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
|
||||
ArrayList<Uri> uris = new ArrayList<Uri>(items.size());
|
||||
for (Parcelable p : items) {
|
||||
if (!(p instanceof Uri)) {
|
||||
Log.d(TAG, "uh, unknown thing " + p);
|
||||
continue;
|
||||
}
|
||||
uris.add((Uri) p);
|
||||
}
|
||||
final ArrayList<Uri> finalUris = uris;
|
||||
Util.runAsync(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
service.enqueueUploadList(finalUris);
|
||||
} catch (RemoteException e) {
|
||||
} finally {
|
||||
stopServiceIfEmpty();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void stopBackgroundWatchers() {
|
||||
synchronized (UploadService.this) {
|
||||
if (mObservers.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Log.d(TAG, "Stopping background watchers...");
|
||||
for (FileObserver fo : mObservers) {
|
||||
fo.stopWatching();
|
||||
}
|
||||
mObservers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateBackgroundWatchers() {
|
||||
stopBackgroundWatchers();
|
||||
if (!mPrefs.autoUpload()) {
|
||||
return;
|
||||
}
|
||||
startBackgroundWatchers();
|
||||
}
|
||||
|
||||
private void startBackgroundWatchers() {
|
||||
Log.d(TAG, "Starting background watchers...");
|
||||
synchronized (UploadService.this) {
|
||||
maybeAddObserver("DCIM/Camera");
|
||||
maybeAddObserver("DCIM/100MEDIA");
|
||||
maybeAddObserver("DCIM/100ANDRO");
|
||||
maybeAddObserver("Eye-Fi");
|
||||
maybeAddObserver("gpx");
|
||||
}
|
||||
}
|
||||
|
||||
// Requires that UploadService.this is locked.
|
||||
private void maybeAddObserver(String suffix) {
|
||||
File f = new File(Environment.getExternalStorageDirectory(), suffix);
|
||||
if (f.exists()) {
|
||||
mObservers.add(new CamliFileObserver(service, f));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
synchronized (this) {
|
||||
Log.d(TAG, "onDestroy of camli UploadService; thread=" + mUploadThread + "; uploading=" + mUploading + "; queue size=" + mFileBytesRemain.size());
|
||||
}
|
||||
super.onDestroy();
|
||||
if (mUploadThread != null) {
|
||||
Log.e(TAG, "Unexpected onDestroy with active upload thread. Killing it.");
|
||||
mUploadThread.interrupt();
|
||||
mUploadThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Called by UploadThread to get stuff to do. Caller owns the returned new
|
||||
// LinkedList. Doesn't return null.
|
||||
LinkedList<QueuedFile> uploadQueue() {
|
||||
synchronized (this) {
|
||||
LinkedList<QueuedFile> copy = new LinkedList<QueuedFile>();
|
||||
copy.addAll(mQueueList);
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
|
||||
void setUploadStatusText(String status) {
|
||||
IStatusCallback cb;
|
||||
synchronized (this) {
|
||||
mLastUploadStatusText = status;
|
||||
cb = mCallback;
|
||||
}
|
||||
try {
|
||||
cb.setUploadStatusText(status);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
|
||||
void setInFlightBytes(int v) {
|
||||
synchronized (this) {
|
||||
mBytesInFlight = v;
|
||||
}
|
||||
broadcastByteStatus();
|
||||
}
|
||||
|
||||
void broadcastByteStatus() {
|
||||
Notification notification = null;
|
||||
synchronized (this) {
|
||||
if (mNotificationBuilder != null) {
|
||||
int kBUploaded = (int)(mBytesUploaded / 1024L);
|
||||
int kBTotal = (int)(mBytesTotal / 1024L);
|
||||
|
||||
mNotificationBuilder.setProgress(kBTotal, kBUploaded, false);
|
||||
notification = mNotificationBuilder.build();
|
||||
}
|
||||
try {
|
||||
mCallback.setByteStatus(mBytesUploaded, mBytesInFlight, mBytesTotal);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
|
||||
if (notification != null) {
|
||||
mNotificationManager.notify(NOTIFY_ID_UPLOADING, notification);
|
||||
}
|
||||
}
|
||||
|
||||
void broadcastFileStatus() {
|
||||
// TODO read mfiles/mcallback under lock and setfilestatus after lock
|
||||
synchronized (this) {
|
||||
try {
|
||||
mCallback.setFileStatus(mFilesUploaded, mFilesInFlight, mFilesTotal);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void broadcastAllState() {
|
||||
synchronized (this) {
|
||||
try {
|
||||
mCallback.setUploading(mUploading);
|
||||
mCallback.setUploadStatusText(mLastUploadStatusText);
|
||||
mCallback.setUploadStatsText(mLastUploadStatsText);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
broadcastFileStatus();
|
||||
broadcastByteStatus();
|
||||
}
|
||||
|
||||
private void onUploadThreadEnded() {
|
||||
synchronized (this) {
|
||||
Log.d(TAG, "UploadThread ended");
|
||||
mNotificationManager.cancel(NOTIFY_ID_UPLOADING);
|
||||
mUploadThread = null;
|
||||
mUploading = false;
|
||||
try {
|
||||
mCallback.setUploading(false);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
stopServiceIfEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback from the UploadThread to the service.
|
||||
*
|
||||
* @param qf
|
||||
* the queued file that was successfully uploaded.
|
||||
*/
|
||||
void onUploadComplete(QueuedFile qf) {
|
||||
Log.d(TAG, "onUploadComplete of " + qf);
|
||||
synchronized (this) {
|
||||
if (!mFileBytesRemain.containsKey(qf)) {
|
||||
Log.w(TAG, "onUploadComplete of un-queued file: " + qf);
|
||||
return;
|
||||
}
|
||||
incrBytes(qf, qf.getSize());
|
||||
mFileBytesRemain.remove(qf);
|
||||
if (mFileBytesRemain.isEmpty()) {
|
||||
// Fill up the percentage bars, since we could get
|
||||
// this event before the periodic stats event.
|
||||
// And at the end, we could kill camput between
|
||||
// getting the final "file uploaded" event and the final
|
||||
// stats event.
|
||||
mFilesUploaded = mFilesTotal;
|
||||
mBytesUploaded = mBytesTotal;
|
||||
mNotificationManager.cancel(NOTIFY_ID_UPLOADING);
|
||||
stopUploadThread();
|
||||
}
|
||||
mQueueList.remove(qf); // TODO: ghetto, linear scan
|
||||
}
|
||||
broadcastAllState();
|
||||
stopServiceIfEmpty();
|
||||
}
|
||||
|
||||
// incrBytes notes that size bytes of qf have been uploaded
|
||||
// and updates mBytesUploaded.
|
||||
private void incrBytes(QueuedFile qf, long size) {
|
||||
synchronized (this) {
|
||||
Long remain = mFileBytesRemain.get(qf);
|
||||
if (remain != null) {
|
||||
long actual = Math.min(size, remain.longValue());
|
||||
mBytesUploaded += actual;
|
||||
mFileBytesRemain.put(qf, remain - actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void stopServiceIfEmpty() {
|
||||
// Convenient place to drop this cache.
|
||||
synchronized (this) {
|
||||
if (mFileBytesRemain.isEmpty() && !mUploading && mUploadThread == null && !mPrefs.autoUpload()) {
|
||||
Log.d(TAG, "stopServiceIfEmpty; stopping");
|
||||
stopSelf();
|
||||
} else {
|
||||
Log.d(TAG, "stopServiceIfEmpty; NOT stopping; " + mFileBytesRemain.isEmpty() + "; " + mUploading + "; " + (mUploadThread != null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ParcelFileDescriptor getFileDescriptor(Uri uri) {
|
||||
ContentResolver cr = getContentResolver();
|
||||
try {
|
||||
return cr.openFileDescriptor(uri, "r");
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.w(TAG, "FileNotFound in getFileDescriptor() for " + uri);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void incrementFilesToUpload(int size) throws RemoteException {
|
||||
synchronized (UploadService.this) {
|
||||
mFilesTotal += size;
|
||||
}
|
||||
broadcastFileStatus();
|
||||
}
|
||||
|
||||
// pathOfURI tries to return the on-disk absolute path of uri.
|
||||
// It may return null if it fails.
|
||||
public String pathOfURI(Uri uri) {
|
||||
if (uri == null) {
|
||||
return null;
|
||||
}
|
||||
if ("file".equals(uri.getScheme())) {
|
||||
return uri.getPath();
|
||||
}
|
||||
String[] proj = { MediaStore.Images.Media.DATA };
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = getContentResolver().query(uri, proj, null, null, null);
|
||||
if (cursor == null) {
|
||||
return null;
|
||||
}
|
||||
cursor.moveToFirst();
|
||||
int columnIndex = cursor.getColumnIndex(proj[0]);
|
||||
return cursor.getString(columnIndex); // might still be null
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final IUploadService.Stub service = new IUploadService.Stub() {
|
||||
|
||||
@Override
|
||||
public int enqueueUploadList(List<Uri> uriList) throws RemoteException {
|
||||
startService(new Intent(UploadService.this, UploadService.class));
|
||||
Log.d(TAG, "enqueuing list of " + uriList.size() + " URIs");
|
||||
incrementFilesToUpload(uriList.size());
|
||||
int goodCount = 0;
|
||||
for (Uri uri : uriList) {
|
||||
goodCount += enqueueSingleUri(uri) ? 1 : 0;
|
||||
}
|
||||
Log.d(TAG, "...goodCount = " + goodCount);
|
||||
return goodCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enqueueUpload(Uri uri) throws RemoteException {
|
||||
startUploadService();
|
||||
incrementFilesToUpload(1);
|
||||
return enqueueSingleUri(uri);
|
||||
}
|
||||
|
||||
private boolean enqueueSingleUri(Uri uri) throws RemoteException {
|
||||
long statSize = 0;
|
||||
{
|
||||
ParcelFileDescriptor pfd = getFileDescriptor(uri);
|
||||
if (pfd == null) {
|
||||
incrementFilesToUpload(-1);
|
||||
stopServiceIfEmpty();
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
statSize = pfd.getStatSize();
|
||||
} finally {
|
||||
try {
|
||||
pfd.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String diskPath = pathOfURI(uri);
|
||||
if (diskPath == null) {
|
||||
Log.e(TAG, "failed to find pathOfURI(" + uri + ")");
|
||||
return false;
|
||||
}
|
||||
Log.d(TAG, "diskPath of " + uri + " = " + diskPath);
|
||||
|
||||
QueuedFile qf = new QueuedFile(uri, statSize, diskPath);
|
||||
|
||||
boolean needResume = false;
|
||||
synchronized (UploadService.this) {
|
||||
if (mFileBytesRemain.containsKey(qf)) {
|
||||
Log.d(TAG, "Dup blob enqueue, ignoring " + qf);
|
||||
stopServiceIfEmpty();
|
||||
return false;
|
||||
}
|
||||
Log.d(TAG, "Enqueueing blob: " + qf);
|
||||
mFileBytesRemain.put(qf, qf.getSize());
|
||||
mQueueList.add(qf);
|
||||
|
||||
if (mFileBytesRemain.size() == 1) {
|
||||
mBytesTotal = 0;
|
||||
mFilesTotal = 0;
|
||||
mBytesUploaded = 0;
|
||||
mFilesUploaded = 0;
|
||||
}
|
||||
mBytesTotal += qf.getSize();
|
||||
mFilesTotal += 1;
|
||||
needResume = !mUploading;
|
||||
|
||||
if (mUploadThread != null) {
|
||||
mUploadThread.enqueueFile(qf);
|
||||
}
|
||||
}
|
||||
broadcastFileStatus();
|
||||
broadcastByteStatus();
|
||||
if (needResume) {
|
||||
resume();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploading() throws RemoteException {
|
||||
synchronized (UploadService.this) {
|
||||
return mUploading;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerCallback(IStatusCallback cb) throws RemoteException {
|
||||
// TODO: permit multiple listeners? when need comes.
|
||||
synchronized (UploadService.this) {
|
||||
if (cb == null) {
|
||||
cb = DummyNullCallback.instance();
|
||||
}
|
||||
mCallback = cb;
|
||||
}
|
||||
broadcastAllState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterCallback(IStatusCallback cb) throws RemoteException {
|
||||
synchronized (UploadService.this) {
|
||||
mCallback = DummyNullCallback.instance();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean resume() throws RemoteException {
|
||||
Log.d(TAG, "Resuming upload...");
|
||||
HostPort hp = mPrefs.hostPort();
|
||||
if (!hp.isValid()) {
|
||||
setUploadStatusText("Upload server not configured.");
|
||||
return false;
|
||||
}
|
||||
|
||||
final PowerManager.WakeLock wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Camli Upload");
|
||||
final WifiManager.WifiLock wifiLock = mWifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "Camli Upload");
|
||||
|
||||
synchronized (UploadService.this) {
|
||||
if (mUploadThread != null) {
|
||||
Log.d(TAG, "Already uploading; aborting resume.");
|
||||
return false;
|
||||
}
|
||||
|
||||
wakeLock.acquire();
|
||||
wifiLock.acquire();
|
||||
|
||||
mNotificationBuilder = new Notification.Builder(UploadService.this);
|
||||
mNotificationBuilder.setOngoing(true)
|
||||
.setContentTitle("Uploading")
|
||||
.setContentText("Camlistore uploader running")
|
||||
.setSmallIcon(android.R.drawable.stat_sys_upload);
|
||||
mNotificationManager.notify(NOTIFY_ID_UPLOADING, mNotificationBuilder.build());
|
||||
|
||||
mUploading = true;
|
||||
mUploadThread = new UploadThread(UploadService.this, hp, mPrefs.trustedCert(), mPrefs.username(), mPrefs.password());
|
||||
mUploadThread.start();
|
||||
|
||||
// Start a thread to release the wakelock...
|
||||
final Thread threadToWatch = mUploadThread;
|
||||
new Thread("UploadThread-waiter") {
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
threadToWatch.join(10000); // 10 seconds
|
||||
} catch (InterruptedException e) {
|
||||
Log.d(TAG, "Interrupt waiting for uploader thread.", e);
|
||||
}
|
||||
synchronized (UploadService.this) {
|
||||
if (threadToWatch.getState() == Thread.State.TERMINATED) {
|
||||
break;
|
||||
}
|
||||
if (threadToWatch == mUploadThread) {
|
||||
Log.d(TAG, "UploadThread-waiter still waiting.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
Log.d(TAG, "UploadThread done; releasing the wakelock");
|
||||
wakeLock.release();
|
||||
wifiLock.release();
|
||||
onUploadThreadEnded();
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
mCallback.setUploading(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pause() throws RemoteException {
|
||||
synchronized (UploadService.this) {
|
||||
if (mUploadThread != null) {
|
||||
stopUploadThread();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queueSize() throws RemoteException {
|
||||
synchronized (UploadService.this) {
|
||||
return mQueueList.size();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopEverything() throws RemoteException {
|
||||
synchronized (UploadService.this) {
|
||||
mNotificationManager.cancel(NOTIFY_ID_UPLOADING);
|
||||
mFileBytesRemain.clear();
|
||||
mQueueList.clear();
|
||||
mLastUploadStatusText = "Stopped";
|
||||
mBytesInFlight = 0;
|
||||
mFilesInFlight = 0;
|
||||
mBytesTotal = 0;
|
||||
mBytesUploaded = 0;
|
||||
mFilesTotal = 0;
|
||||
mFilesUploaded = 0;
|
||||
stopUploadThread(); // recursive lock: okay
|
||||
}
|
||||
broadcastAllState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBackgroundWatchersEnabled(boolean enabled) throws RemoteException {
|
||||
if (enabled) {
|
||||
startUploadService();
|
||||
UploadService.this.stopBackgroundWatchers();
|
||||
UploadService.this.startBackgroundWatchers();
|
||||
} else {
|
||||
UploadService.this.stopBackgroundWatchers();
|
||||
stopServiceIfEmpty();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public void onChunkUploaded(CamputChunkUploadedMessage msg) {
|
||||
Log.d(TAG, "chunked uploaded for " + msg.queuedFile() + " with size " + msg.size());
|
||||
synchronized (UploadService.this) {
|
||||
incrBytes(msg.queuedFile(), msg.size());
|
||||
}
|
||||
broadcastAllState();
|
||||
}
|
||||
|
||||
public void onStatReceived(String stat, long value) {
|
||||
String v;
|
||||
synchronized (UploadService.this) {
|
||||
if (stat == null) {
|
||||
mStatValue.clear();
|
||||
} else {
|
||||
mStatValue.put(stat, value);
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Entry<String, Long> ent : mStatValue.entrySet()) {
|
||||
sb.append(ent.getKey());
|
||||
sb.append(": ");
|
||||
sb.append(ent.getValue());
|
||||
sb.append("\n");
|
||||
}
|
||||
v = sb.toString();
|
||||
mLastUploadStatsText = v;
|
||||
}
|
||||
try {
|
||||
mCallback.setUploadStatsText(v);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
|
||||
protected void stopUploadThread() {
|
||||
synchronized (UploadService.this) {
|
||||
mNotificationManager.cancel(NOTIFY_ID_UPLOADING);
|
||||
if (mUploadThread != null) {
|
||||
mUploadThread.stopUploads();
|
||||
mUploadThread = null;
|
||||
try {
|
||||
mCallback.setUploading(false);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
mUploading = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void onStatsReceived(CamputStatsMessage msg) {
|
||||
synchronized (UploadService.this) {
|
||||
mBytesTotal = msg.totalBytes();
|
||||
mFilesTotal = (int) msg.totalFiles();
|
||||
mBytesUploaded = msg.skippedBytes() + msg.uploadedBytes();
|
||||
mFilesUploaded = (int) (msg.skippedFiles() + msg.uploadedFiles());
|
||||
}
|
||||
broadcastAllState();
|
||||
}
|
||||
}
|
||||
409
vendor/github.com/camlistore/camlistore/clients/android/src/org/camlistore/UploadThread.java
generated
vendored
Normal file
@@ -0,0 +1,409 @@
|
||||
/*
|
||||
Copyright 2011 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package org.camlistore;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.util.HashMap;
|
||||
import java.util.ListIterator;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public class UploadThread extends Thread {
|
||||
private static final String TAG = "UploadThread";
|
||||
|
||||
private final UploadService mService;
|
||||
private final HostPort mHostPort;
|
||||
private final String mTrustedCert;
|
||||
private final String mUsername;
|
||||
private final String mPassword;
|
||||
private final LinkedBlockingQueue<UploadThreadMessage> msgCh = new LinkedBlockingQueue<UploadThreadMessage>();
|
||||
|
||||
AtomicReference<Process> goProcess = new AtomicReference<Process>();
|
||||
AtomicReference<OutputStream> toChildRef = new AtomicReference<OutputStream>();
|
||||
HashMap<String, QueuedFile> mQueuedFile = new HashMap<String, QueuedFile>(); // guarded
|
||||
// by
|
||||
// itself
|
||||
|
||||
private final Object stdinLock = new Object(); // guards setting and writing
|
||||
// to stdinWriter
|
||||
private BufferedWriter stdinWriter;
|
||||
|
||||
public UploadThread(UploadService uploadService, HostPort hp, String trustedCert, String username, String password) {
|
||||
mService = uploadService;
|
||||
mHostPort = hp;
|
||||
mTrustedCert = trustedCert != null ? trustedCert.toLowerCase().trim() : "";
|
||||
mUsername = username;
|
||||
mPassword = password;
|
||||
}
|
||||
|
||||
public void stopUploads() {
|
||||
Process p = goProcess.get();
|
||||
if (p == null) {
|
||||
return;
|
||||
}
|
||||
synchronized (stdinLock) {
|
||||
if (stdinWriter == null) {
|
||||
// force kill. confused.
|
||||
p.destroy();
|
||||
goProcess.set(null);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
stdinWriter.close();
|
||||
Log.d(TAG, "Closed camput's stdin");
|
||||
stdinWriter = null;
|
||||
} catch (IOException e) {
|
||||
p.destroy(); // force kill
|
||||
goProcess.set(null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Unnecessary paranoia, never seen in practice:
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(750, 0);
|
||||
stopUploads(); // force kill if still alive.
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
}
|
||||
|
||||
private String binaryPath(String suffix) {
|
||||
return mService.getBaseContext().getFilesDir().getAbsolutePath() + "/" + suffix;
|
||||
}
|
||||
|
||||
private void status(String st) {
|
||||
Log.d(TAG, st);
|
||||
mService.setUploadStatusText(st);
|
||||
}
|
||||
|
||||
// An UploadThreadMessage can be sent on msgCh and read by the run() method
|
||||
// in
|
||||
// until it's time to quit the thread.
|
||||
private static class UploadThreadMessage {
|
||||
}
|
||||
|
||||
private static class ProcessExitedMessage extends UploadThreadMessage {
|
||||
public int code;
|
||||
|
||||
public ProcessExitedMessage(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean enqueueFile(QueuedFile qf) {
|
||||
String diskPath = qf.getDiskPath();
|
||||
if (diskPath == null) {
|
||||
Log.d(TAG, "file has no disk path: " + qf);
|
||||
return false;
|
||||
}
|
||||
synchronized (stdinLock) {
|
||||
if (stdinWriter == null) {
|
||||
return false;
|
||||
}
|
||||
synchronized (mQueuedFile) {
|
||||
mQueuedFile.put(diskPath, qf);
|
||||
}
|
||||
try {
|
||||
stdinWriter.write(diskPath + "\n");
|
||||
stdinWriter.flush();
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Failed to write " + diskPath + " to camput stdin: " + e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Log.d(TAG, "Running");
|
||||
if (!mHostPort.isValid()) {
|
||||
Log.d(TAG, "host/port is invalid");
|
||||
return;
|
||||
}
|
||||
status("Running UploadThread for " + mHostPort);
|
||||
|
||||
mService.onStatReceived(null, 0);
|
||||
|
||||
Process process = null;
|
||||
try {
|
||||
ProcessBuilder pb = new ProcessBuilder();
|
||||
pb.command(binaryPath("camput.bin"), "--server=" + mHostPort.urlPrefix(), "file", "-stdinargs", "-vivify");
|
||||
pb.redirectErrorStream(false);
|
||||
pb.environment().put("CAMLI_AUTH", "userpass:" + mUsername + ":" + mPassword);
|
||||
pb.environment().put("CAMLI_TRUSTED_CERT", mTrustedCert);
|
||||
pb.environment().put("CAMLI_CACHE_DIR", mService.getCacheDir().getAbsolutePath());
|
||||
pb.environment().put("CAMPUT_ANDROID_OUTPUT", "1");
|
||||
process = pb.start();
|
||||
goProcess.set(process);
|
||||
synchronized (stdinLock) {
|
||||
stdinWriter = new BufferedWriter(new OutputStreamWriter(process.getOutputStream(), "UTF-8"));
|
||||
}
|
||||
new CopyToAndroidLogThread("stderr", process.getErrorStream()).start();
|
||||
new ParseCamputOutputThread(process, mService).start();
|
||||
new WaitForProcessThread(process).start();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
ListIterator<QueuedFile> iter = mService.uploadQueue().listIterator();
|
||||
while (iter.hasNext()) {
|
||||
enqueueFile(iter.next());
|
||||
}
|
||||
|
||||
// Loop forever reading from msgCh
|
||||
while (true) {
|
||||
UploadThreadMessage msg = null;
|
||||
try {
|
||||
msg = msgCh.poll(10, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
continue;
|
||||
}
|
||||
if (msg instanceof ProcessExitedMessage) {
|
||||
status("Upload process ended.");
|
||||
ProcessExitedMessage pem = (ProcessExitedMessage) msg;
|
||||
Log.d(TAG, "Loop exiting; code was = " + pem.code);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "CHUNK_UPLOADED %d %s %s\n", sb.Size, blob, asr.path
|
||||
private final static Pattern chunkUploadedPattern = Pattern.compile("^CHUNK_UPLOADED (\\d+) (\\S+) (.+)");
|
||||
|
||||
public class CamputChunkUploadedMessage {
|
||||
private final String mFilename;
|
||||
private final long mSize;
|
||||
|
||||
public CamputChunkUploadedMessage(String line) {
|
||||
Matcher m = chunkUploadedPattern.matcher(line);
|
||||
if (!m.matches()) {
|
||||
throw new RuntimeException("bogus CamputChunkMessage: " + line);
|
||||
}
|
||||
mSize = Long.parseLong(m.group(1));
|
||||
mFilename = m.group(3);
|
||||
}
|
||||
|
||||
public QueuedFile queuedFile() {
|
||||
synchronized (mQueuedFile) {
|
||||
return mQueuedFile.get(mFilename);
|
||||
}
|
||||
}
|
||||
|
||||
public long size() {
|
||||
return mSize;
|
||||
}
|
||||
}
|
||||
|
||||
// STAT %s %d\n
|
||||
private final static Pattern statPattern = Pattern.compile("^STAT (\\S+) (\\d+)\\b");
|
||||
|
||||
public class CamputStatMessage {
|
||||
private final Matcher mm;
|
||||
|
||||
public CamputStatMessage(String line) {
|
||||
mm = statPattern.matcher(line);
|
||||
if (!mm.matches()) {
|
||||
throw new RuntimeException("bogus CamputStatMessage: " + line);
|
||||
}
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return mm.group(1);
|
||||
}
|
||||
|
||||
public long value() {
|
||||
return Long.parseLong(mm.group(2));
|
||||
}
|
||||
}
|
||||
|
||||
// STATS nfile=%d nbyte=%d skfile=%d skbyte=%d upfile=%d upbyte=%d\n
|
||||
private final static Pattern statsPattern = Pattern.compile("^STATS nfile=(\\d+) nbyte=(\\d+) skfile=(\\d+) skbyte=(\\d+) upfile=(\\d+) upbyte=(\\d+)");
|
||||
|
||||
public class CamputStatsMessage {
|
||||
private final Matcher mm;
|
||||
|
||||
public CamputStatsMessage(String line) {
|
||||
mm = statsPattern.matcher(line);
|
||||
if (!mm.matches()) {
|
||||
throw new RuntimeException("bogus CamputStatsMessage: " + line);
|
||||
}
|
||||
}
|
||||
|
||||
private long field(int n) {
|
||||
return Long.parseLong(mm.group(n));
|
||||
}
|
||||
|
||||
public long totalFiles() {
|
||||
return field(1);
|
||||
}
|
||||
|
||||
public long totalBytes() {
|
||||
return field(2);
|
||||
}
|
||||
|
||||
public long skippedFiles() {
|
||||
return field(3);
|
||||
}
|
||||
|
||||
public long skippedBytes() {
|
||||
return field(4);
|
||||
}
|
||||
|
||||
public long uploadedFiles() {
|
||||
return field(5);
|
||||
}
|
||||
|
||||
public long uploadedBytes() {
|
||||
return field(6);
|
||||
}
|
||||
}
|
||||
|
||||
private class ParseCamputOutputThread extends Thread {
|
||||
private final BufferedReader mBufIn;
|
||||
private final UploadService mService;
|
||||
private final static String TAG = UploadThread.TAG + "/camput-out";
|
||||
private final static boolean DEBUG_CAMPUT_ACTIVITY = false;
|
||||
|
||||
public ParseCamputOutputThread(Process process, UploadService service) {
|
||||
mService = service;
|
||||
mBufIn = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
String line = null;
|
||||
try {
|
||||
line = mBufIn.readLine();
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Exception reading camput's stdout: " + e.toString());
|
||||
return;
|
||||
}
|
||||
if (DEBUG_CAMPUT_ACTIVITY) {
|
||||
Log.d(TAG, "camput: " + line);
|
||||
}
|
||||
if (line == null) {
|
||||
// EOF
|
||||
return;
|
||||
}
|
||||
if (line.startsWith("CHUNK_UPLOADED ")) {
|
||||
CamputChunkUploadedMessage msg = new CamputChunkUploadedMessage(line);
|
||||
mService.onChunkUploaded(msg);
|
||||
continue;
|
||||
}
|
||||
if (line.startsWith("STATS ")) {
|
||||
CamputStatsMessage msg = new CamputStatsMessage(line);
|
||||
mService.onStatsReceived(msg);
|
||||
continue;
|
||||
}
|
||||
if (line.startsWith("STAT ")) {
|
||||
CamputStatMessage msg = new CamputStatMessage(line);
|
||||
mService.onStatReceived(msg.name(), msg.value());
|
||||
continue;
|
||||
}
|
||||
if (line.startsWith("FILE_UPLOADED ")) {
|
||||
String filename = line.substring(14).trim();
|
||||
QueuedFile qf = null;
|
||||
synchronized (mQueuedFile) {
|
||||
qf = mQueuedFile.get(filename);
|
||||
if (qf != null) {
|
||||
mQueuedFile.remove(filename);
|
||||
}
|
||||
}
|
||||
if (qf != null) {
|
||||
mService.onUploadComplete(qf);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
Log.d(TAG, "camput said unknown line: " + line);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private class WaitForProcessThread extends Thread {
|
||||
private final Process mProcess;
|
||||
|
||||
public WaitForProcessThread(Process p) {
|
||||
mProcess = p;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Log.d(TAG, "Waiting for camput process.");
|
||||
try {
|
||||
mProcess.waitFor();
|
||||
} catch (InterruptedException e) {
|
||||
Log.d(TAG, "Interrupted waiting for camput");
|
||||
msgCh.offer(new ProcessExitedMessage(-1));
|
||||
return;
|
||||
}
|
||||
Log.d(TAG, "Exit status of camput = " + mProcess.exitValue());
|
||||
msgCh.offer(new ProcessExitedMessage(mProcess.exitValue()));
|
||||
}
|
||||
}
|
||||
|
||||
// CopyToAndroidLogThread copies the camput child process's stderr
|
||||
// to Android's log.
|
||||
private static class CopyToAndroidLogThread extends Thread {
|
||||
private final BufferedReader mBufIn;
|
||||
private final String mStream;
|
||||
|
||||
public CopyToAndroidLogThread(String stream, InputStream in) {
|
||||
mBufIn = new BufferedReader(new InputStreamReader(in));
|
||||
mStream = stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
String tag = TAG + "/" + mStream + "-child";
|
||||
while (true) {
|
||||
String line = null;
|
||||
try {
|
||||
line = mBufIn.readLine();
|
||||
} catch (IOException e) {
|
||||
Log.d(tag, "Exception: " + e.toString());
|
||||
return;
|
||||
}
|
||||
if (line == null) {
|
||||
// EOF
|
||||
return;
|
||||
}
|
||||
Log.d(tag, line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
149
vendor/github.com/camlistore/camlistore/clients/android/src/org/camlistore/Util.java
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
Copyright 2011 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package org.camlistore;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Looper;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
public class Util {
|
||||
private static final String TAG = "Camli_Util";
|
||||
|
||||
public static String slurp(InputStream in) throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
byte[] b = new byte[4096];
|
||||
for (int n; (n = in.read(b)) != -1;) {
|
||||
sb.append(new String(b, 0, n));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static byte[] slurpToByteArray(InputStream inputStream) throws IOException {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[4096];
|
||||
for (int numRead; (numRead = inputStream.read(buffer)) != -1;) {
|
||||
outputStream.write(buffer, 0, numRead);
|
||||
}
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
|
||||
public static void copyFile(File fromFile, File toFile) throws IOException {
|
||||
FileInputStream inputStream = new FileInputStream(fromFile);
|
||||
FileOutputStream outputStream = new FileOutputStream(toFile);
|
||||
byte[] buffer = new byte[4096];
|
||||
for (int numRead; (numRead = inputStream.read(buffer)) != -1;)
|
||||
outputStream.write(buffer, 0, numRead);
|
||||
inputStream.close();
|
||||
outputStream.close();
|
||||
}
|
||||
|
||||
public static void runAsync(final Runnable r) {
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... unused) {
|
||||
r.run();
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
public static boolean onMainThread() {
|
||||
return Looper.myLooper() == Looper.getMainLooper();
|
||||
}
|
||||
|
||||
public static void assertMainThread() {
|
||||
if (!onMainThread()) {
|
||||
throw new RuntimeException("Assert: unexpected call off the main thread");
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertNotMainThread() {
|
||||
if (onMainThread()) {
|
||||
throw new RuntimeException("Assert: unexpected call on main thread");
|
||||
}
|
||||
}
|
||||
|
||||
// Asserts that |lock| is held by the current thread.
|
||||
public static void assertLockIsHeld(ReentrantLock lock) {
|
||||
if (!lock.isHeldByCurrentThread()) {
|
||||
throw new RuntimeException("Assert: mandatory lock isn't held by current thread");
|
||||
}
|
||||
}
|
||||
|
||||
// Asserts that |lock| is not held by the current thread.
|
||||
public static void assertLockIsNotHeld(ReentrantLock lock) {
|
||||
if (lock.isHeldByCurrentThread()) {
|
||||
throw new RuntimeException("Assert: lock is held by current thread but shouldn't be");
|
||||
}
|
||||
}
|
||||
|
||||
private static final String HEX = "0123456789abcdef";
|
||||
|
||||
public static String getHex(byte[] raw) {
|
||||
if (raw == null) {
|
||||
return null;
|
||||
}
|
||||
final StringBuilder hex = new StringBuilder(2 * raw.length);
|
||||
for (final byte b : raw) {
|
||||
hex.append(HEX.charAt((b & 0xF0) >> 4)).append(
|
||||
HEX.charAt((b & 0x0F)));
|
||||
}
|
||||
return hex.toString();
|
||||
}
|
||||
|
||||
// Requires that the fd be seeked to the beginning.
|
||||
public static String getSha1(FileDescriptor fd) {
|
||||
MessageDigest md;
|
||||
try {
|
||||
md = MessageDigest.getInstance("SHA-1");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
byte[] b = new byte[4096];
|
||||
FileInputStream fis = new FileInputStream(fd);
|
||||
InputStream is = new BufferedInputStream(fis, 4096);
|
||||
try {
|
||||
for (int n; (n = is.read(b)) != -1;) {
|
||||
md.update(b, 0, n);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "IOException while computing SHA-1");
|
||||
return null;
|
||||
}
|
||||
byte[] sha1hash = new byte[40];
|
||||
sha1hash = md.digest();
|
||||
return getHex(sha1hash);
|
||||
}
|
||||
|
||||
public static String getBasicAuthHeaderValue(String username, String password) {
|
||||
return "Basic " + Base64.encodeToString((username + ":" + password).getBytes(),
|
||||
Base64.NO_WRAP);
|
||||
}
|
||||
}
|
||||
90
vendor/github.com/camlistore/camlistore/clients/android/src/org/camlistore/WifiPowerReceiver.java
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
Copyright 2011 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package org.camlistore;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.wifi.WifiInfo;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.util.Log;
|
||||
|
||||
public class WifiPowerReceiver extends BroadcastReceiver {
|
||||
private static final String TAG = "WifiPowerReceiver";
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
Log.d(TAG, "intent: " + intent);
|
||||
if (Intent.ACTION_POWER_CONNECTED.equals(action)) {
|
||||
Intent cmd = new Intent(UploadService.INTENT_POWER_CONNECTED);
|
||||
cmd.setClass(context, UploadService.class);
|
||||
context.startService(cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Intent.ACTION_POWER_DISCONNECTED.equals(action)) {
|
||||
Intent cmd = new Intent(UploadService.INTENT_POWER_DISCONNECTED);
|
||||
cmd.setClass(context, UploadService.class);
|
||||
context.startService(cmd);
|
||||
}
|
||||
|
||||
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
|
||||
boolean wifi = onWifi(context);
|
||||
Log.d(TAG, "onWifi = " + wifi);
|
||||
Intent cmd = new Intent(wifi ? UploadService.INTENT_NETWORK_WIFI : UploadService.INTENT_NETWORK_NOT_WIFI);
|
||||
String ssid = getSSID(context);
|
||||
cmd.putExtra("SSID", ssid);
|
||||
Log.d(TAG, "extra ssid (chk)= " + cmd.getStringExtra("SSID"));
|
||||
cmd.setClass(context, UploadService.class);
|
||||
context.startService(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean onPower(Context context) {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean onWifi(Context context) {
|
||||
NetworkInfo ni = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
|
||||
if (ni != null && ni.isConnected()
|
||||
&& (ni.getType() == ConnectivityManager.TYPE_WIFI || ni.getType() == ConnectivityManager.TYPE_ETHERNET)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String getSSID(Context context) {
|
||||
NetworkInfo ni = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
|
||||
if (ni != null && ni.isConnected()
|
||||
&& (ni.getType() == ConnectivityManager.TYPE_WIFI || ni.getType() == ConnectivityManager.TYPE_ETHERNET)) {
|
||||
WifiManager wifiMgr = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
|
||||
if (wifiMgr != null) {
|
||||
WifiInfo wifiInfo = wifiMgr.getConnectionInfo();
|
||||
String ssid = wifiInfo.getSSID();
|
||||
if (ssid.startsWith("\"") && ssid.endsWith("\"")){
|
||||
ssid = ssid.substring(1, ssid.length()-1);
|
||||
}
|
||||
return ssid;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
10
vendor/github.com/camlistore/camlistore/clients/android/test/.classpath
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/camlistore"/>
|
||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
|
||||
<classpathentry kind="output" path="bin/classes"/>
|
||||
</classpath>
|
||||
34
vendor/github.com/camlistore/camlistore/clients/android/test/.project
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>camlistoreTest</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
<project>camlistore</project>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
4
vendor/github.com/camlistore/camlistore/clients/android/test/.settings/org.eclipse.jdt.core.prefs
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
|
||||
org.eclipse.jdt.core.compiler.compliance=1.6
|
||||
org.eclipse.jdt.core.compiler.source=1.6
|
||||
12
vendor/github.com/camlistore/camlistore/clients/android/test/AndroidManifest.xml
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.camlistore.test"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0">
|
||||
<application android:icon="@drawable/icon" android:label="@string/app_name">
|
||||
|
||||
<uses-library android:name="android.test.runner" />
|
||||
</application>
|
||||
|
||||
<instrumentation android:targetPackage="org.camlistore" android:name="android.test.InstrumentationTestRunner" />
|
||||
</manifest>
|
||||
21
vendor/github.com/camlistore/camlistore/clients/android/test/build.properties
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# This file is used to override default values used by the Ant build system.
|
||||
#
|
||||
# This file must be checked in Version Control Systems, as it is
|
||||
# integral to the build system of your project.
|
||||
|
||||
# This file is only used by the Ant script.
|
||||
|
||||
# You can use this to override default values such as
|
||||
# 'source.dir' for the location of your java source folder and
|
||||
# 'out.dir' for the location of your output folder.
|
||||
|
||||
# You can also use it define how the release builds are signed by declaring
|
||||
# the following properties:
|
||||
# 'key.store' for the location of your keystore and
|
||||
# 'key.alias' for the name of the key to use.
|
||||
# The password will be asked during the build when you use the 'release' target.
|
||||
|
||||
tested.project.dir=..
|
||||
|
||||
out.dir=build
|
||||
gen.dir=build/gen
|
||||
73
vendor/github.com/camlistore/camlistore/clients/android/test/build.xml
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="CamliActivityTest" default="help">
|
||||
|
||||
<!-- The local.properties file is created and updated by the 'android' tool.
|
||||
It contains the path to the SDK. It should *NOT* be checked in in Version
|
||||
Control Systems. -->
|
||||
<property file="local.properties" />
|
||||
|
||||
<!-- The build.properties file can be created by you and is never touched
|
||||
by the 'android' tool. This is the place to change some of the default property values
|
||||
used by the Ant rules.
|
||||
Here are some properties you may want to change/update:
|
||||
|
||||
application.package
|
||||
the name of your application package as defined in the manifest. Used by the
|
||||
'uninstall' rule.
|
||||
source.dir
|
||||
the name of the source directory. Default is 'src'.
|
||||
out.dir
|
||||
the name of the output directory. Default is 'bin'.
|
||||
|
||||
Properties related to the SDK location or the project target should be updated
|
||||
using the 'android' tool with the 'update' action.
|
||||
|
||||
This file is an integral part of the build system for your application and
|
||||
should be checked in in Version Control Systems.
|
||||
|
||||
-->
|
||||
<property file="build.properties" />
|
||||
|
||||
<!-- The default.properties file is created and updated by the 'android' tool, as well
|
||||
as ADT.
|
||||
This file is an integral part of the build system for your application and
|
||||
should be checked in in Version Control Systems. -->
|
||||
<property file="default.properties" />
|
||||
|
||||
<!-- Custom Android task to deal with the project target, and import the proper rules.
|
||||
This requires ant 1.6.0 or above. -->
|
||||
<path id="android.antlibs">
|
||||
<pathelement path="${sdk.dir}/tools/lib/anttasks.jar" />
|
||||
<pathelement path="${sdk.dir}/tools/lib/sdklib.jar" />
|
||||
<pathelement path="${sdk.dir}/tools/lib/androidprefs.jar" />
|
||||
<pathelement path="${sdk.dir}/tools/lib/apkbuilder.jar" />
|
||||
<pathelement path="${sdk.dir}/tools/lib/jarutils.jar" />
|
||||
</path>
|
||||
|
||||
<taskdef name="setup"
|
||||
classname="com.android.ant.SetupTask"
|
||||
classpathref="android.antlibs" />
|
||||
|
||||
<!-- Execute the Android Setup task that will setup some properties specific to the target,
|
||||
and import the build rules files.
|
||||
|
||||
The rules file is imported from
|
||||
<SDK>/platforms/<target_platform>/templates/android_rules.xml
|
||||
|
||||
To customize some build steps for your project:
|
||||
- copy the content of the main node <project> from android_rules.xml
|
||||
- paste it in this build.xml below the <setup /> task.
|
||||
- disable the import by changing the setup task below to <setup import="false" />
|
||||
|
||||
This will ensure that the properties are setup correctly but that your customized
|
||||
build steps are used.
|
||||
-->
|
||||
<setup />
|
||||
|
||||
<!-- See:
|
||||
http://www.alittlemadness.com/2010/05/31/setting-up-an-android-project-build/
|
||||
and:
|
||||
http://code.google.com/p/android/issues/detail?id=8001
|
||||
-->
|
||||
<property name="extensible.classpath" value="${tested.project.absolute.dir}/${out.dir}/classes"/>
|
||||
</project>
|
||||
11
vendor/github.com/camlistore/camlistore/clients/android/test/default.properties
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
# This file is automatically generated by Android Tools.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file must be checked in Version Control Systems.
|
||||
#
|
||||
# To customize properties used by the Ant build system use,
|
||||
# "build.properties", and override values to adapt the script to your
|
||||
# project structure.
|
||||
|
||||
# Project target.
|
||||
target=android-4
|
||||
14
vendor/github.com/camlistore/camlistore/clients/android/test/project.properties
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
# This file is automatically generated by Android Tools.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file must be checked in Version Control Systems.
|
||||
#
|
||||
# To customize properties used by the Ant build system edit
|
||||
# "ant.properties", and override values to adapt the script to your
|
||||
# project structure.
|
||||
#
|
||||
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||
|
||||
# Project target.
|
||||
target=android-10
|
||||
BIN
vendor/github.com/camlistore/camlistore/clients/android/test/res/drawable-hdpi/icon.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
vendor/github.com/camlistore/camlistore/clients/android/test/res/drawable-ldpi/icon.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
vendor/github.com/camlistore/camlistore/clients/android/test/res/drawable-mdpi/icon.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
12
vendor/github.com/camlistore/camlistore/clients/android/test/res/layout/main.xml
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
>
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/hello"
|
||||
/>
|
||||
</LinearLayout>
|
||||
5
vendor/github.com/camlistore/camlistore/clients/android/test/res/values/strings.xml
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="hello">Hello World!</string>
|
||||
<string name="app_name">camlistoreTest</string>
|
||||
</resources>
|
||||
32
vendor/github.com/camlistore/camlistore/clients/android/test/src/org/camlistore/CamliActivityTest.java
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
Copyright 2011 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package org.camlistore;
|
||||
|
||||
import android.test.ActivityInstrumentationTestCase2;
|
||||
|
||||
public class CamliActivityTest extends ActivityInstrumentationTestCase2<CamliActivity> {
|
||||
|
||||
public CamliActivityTest(String pkg, Class<CamliActivity> activityClass) {
|
||||
super(pkg, activityClass);
|
||||
// TODO Auto-generated constructor stub
|
||||
}
|
||||
|
||||
public void testSanity() {
|
||||
assertEquals(2, 1 + 1);
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
||||
183
vendor/github.com/camlistore/camlistore/clients/chrome/clip-it-good/Crypto.js
generated
vendored
Normal file
@@ -0,0 +1,183 @@
|
||||
// From http://code.google.com/p/crypto-js/
|
||||
// License: http://www.opensource.org/licenses/bsd-license.php
|
||||
//
|
||||
// Copyright (c) 2009, Jeff Mott. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer. Redistributions in binary
|
||||
// form must reproduce the above copyright notice, this list of conditions and
|
||||
// the following disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution. Neither the name Crypto-JS nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from this
|
||||
// software without specific prior written permission. THIS SOFTWARE IS PROVIDED
|
||||
// BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
// EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
if (typeof Crypto == "undefined" || ! Crypto.util)
|
||||
{
|
||||
(function(){
|
||||
|
||||
var base64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
// Global Crypto object
|
||||
var Crypto = window.Crypto = {};
|
||||
|
||||
// Crypto utilities
|
||||
var util = Crypto.util = {
|
||||
|
||||
// Bit-wise rotate left
|
||||
rotl: function (n, b) {
|
||||
return (n << b) | (n >>> (32 - b));
|
||||
},
|
||||
|
||||
// Bit-wise rotate right
|
||||
rotr: function (n, b) {
|
||||
return (n << (32 - b)) | (n >>> b);
|
||||
},
|
||||
|
||||
// Swap big-endian to little-endian and vice versa
|
||||
endian: function (n) {
|
||||
|
||||
// If number given, swap endian
|
||||
if (n.constructor == Number) {
|
||||
return util.rotl(n, 8) & 0x00FF00FF |
|
||||
util.rotl(n, 24) & 0xFF00FF00;
|
||||
}
|
||||
|
||||
// Else, assume array and swap all items
|
||||
for (var i = 0; i < n.length; i++)
|
||||
n[i] = util.endian(n[i]);
|
||||
return n;
|
||||
|
||||
},
|
||||
|
||||
// Generate an array of any length of random bytes
|
||||
randomBytes: function (n) {
|
||||
for (var bytes = []; n > 0; n--)
|
||||
bytes.push(Math.floor(Math.random() * 256));
|
||||
return bytes;
|
||||
},
|
||||
|
||||
// Convert a byte array to big-endian 32-bit words
|
||||
bytesToWords: function (bytes) {
|
||||
for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8)
|
||||
words[b >>> 5] |= bytes[i] << (24 - b % 32);
|
||||
return words;
|
||||
},
|
||||
|
||||
// Convert big-endian 32-bit words to a byte array
|
||||
wordsToBytes: function (words) {
|
||||
for (var bytes = [], b = 0; b < words.length * 32; b += 8)
|
||||
bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF);
|
||||
return bytes;
|
||||
},
|
||||
|
||||
// Convert a byte array to a hex string
|
||||
bytesToHex: function (bytes) {
|
||||
for (var hex = [], i = 0; i < bytes.length; i++) {
|
||||
hex.push((bytes[i] >>> 4).toString(16));
|
||||
hex.push((bytes[i] & 0xF).toString(16));
|
||||
}
|
||||
return hex.join("");
|
||||
},
|
||||
|
||||
// Convert a hex string to a byte array
|
||||
hexToBytes: function (hex) {
|
||||
for (var bytes = [], c = 0; c < hex.length; c += 2)
|
||||
bytes.push(parseInt(hex.substr(c, 2), 16));
|
||||
return bytes;
|
||||
},
|
||||
|
||||
// Convert a byte array to a base-64 string
|
||||
bytesToBase64: function (bytes) {
|
||||
|
||||
// Use browser-native function if it exists
|
||||
if (typeof btoa == "function") return btoa(Binary.bytesToString(bytes));
|
||||
|
||||
for(var base64 = [], i = 0; i < bytes.length; i += 3) {
|
||||
var triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
|
||||
for (var j = 0; j < 4; j++) {
|
||||
if (i * 8 + j * 6 <= bytes.length * 8)
|
||||
base64.push(base64map.charAt((triplet >>> 6 * (3 - j)) & 0x3F));
|
||||
else base64.push("=");
|
||||
}
|
||||
}
|
||||
|
||||
return base64.join("");
|
||||
|
||||
},
|
||||
|
||||
// Convert a base-64 string to a byte array
|
||||
base64ToBytes: function (base64) {
|
||||
|
||||
// Use browser-native function if it exists
|
||||
if (typeof atob == "function") return Binary.stringToBytes(atob(base64));
|
||||
|
||||
// Remove non-base-64 characters
|
||||
base64 = base64.replace(/[^A-Z0-9+\/]/ig, "");
|
||||
|
||||
for (var bytes = [], i = 0, imod4 = 0; i < base64.length; imod4 = ++i % 4) {
|
||||
if (imod4 == 0) continue;
|
||||
bytes.push(((base64map.indexOf(base64.charAt(i - 1)) & (Math.pow(2, -2 * imod4 + 8) - 1)) << (imod4 * 2)) |
|
||||
(base64map.indexOf(base64.charAt(i)) >>> (6 - imod4 * 2)));
|
||||
}
|
||||
|
||||
return bytes;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Crypto mode namespace
|
||||
Crypto.mode = {};
|
||||
|
||||
// Crypto character encodings
|
||||
var charenc = Crypto.charenc = {};
|
||||
|
||||
// UTF-8 encoding
|
||||
var UTF8 = charenc.UTF8 = {
|
||||
|
||||
// Convert a string to a byte array
|
||||
stringToBytes: function (str) {
|
||||
return Binary.stringToBytes(unescape(encodeURIComponent(str)));
|
||||
},
|
||||
|
||||
// Convert a byte array to a string
|
||||
bytesToString: function (bytes) {
|
||||
return decodeURIComponent(escape(Binary.bytesToString(bytes)));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Binary encoding
|
||||
var Binary = charenc.Binary = {
|
||||
|
||||
// Convert a string to a byte array
|
||||
stringToBytes: function (str) {
|
||||
for (var bytes = [], i = 0; i < str.length; i++)
|
||||
bytes.push(str.charCodeAt(i));
|
||||
return bytes;
|
||||
},
|
||||
|
||||
// Convert a byte array to a string
|
||||
bytesToString: function (bytes) {
|
||||
for (var str = [], i = 0; i < bytes.length; i++)
|
||||
str.push(String.fromCharCode(bytes[i]));
|
||||
return str.join("");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
})();
|
||||
}
|
||||
13
vendor/github.com/camlistore/camlistore/clients/chrome/clip-it-good/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
Copyright 2010 Brett Slatkin
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
109
vendor/github.com/camlistore/camlistore/clients/chrome/clip-it-good/SHA1.js
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
// From http://code.google.com/p/crypto-js/
|
||||
// License: http://www.opensource.org/licenses/bsd-license.php
|
||||
//
|
||||
// Copyright (c) 2009, Jeff Mott. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer. Redistributions in binary
|
||||
// form must reproduce the above copyright notice, this list of conditions and
|
||||
// the following disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution. Neither the name Crypto-JS nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from this
|
||||
// software without specific prior written permission. THIS SOFTWARE IS PROVIDED
|
||||
// BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
// EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
(function(){
|
||||
|
||||
// Shortcuts
|
||||
var C = Crypto,
|
||||
util = C.util,
|
||||
charenc = C.charenc,
|
||||
UTF8 = charenc.UTF8,
|
||||
Binary = charenc.Binary;
|
||||
|
||||
// Public API
|
||||
var SHA1 = C.SHA1 = function (message, options) {
|
||||
var digestbytes = util.wordsToBytes(SHA1._sha1(message));
|
||||
return options && options.asBytes ? digestbytes :
|
||||
options && options.asString ? Binary.bytesToString(digestbytes) :
|
||||
util.bytesToHex(digestbytes);
|
||||
};
|
||||
|
||||
// The core
|
||||
SHA1._sha1 = function (message) {
|
||||
|
||||
// Convert to byte array
|
||||
if (message.constructor == String) message = UTF8.stringToBytes(message);
|
||||
/* else, assume byte array already */
|
||||
|
||||
var m = util.bytesToWords(message),
|
||||
l = message.length * 8,
|
||||
w = [],
|
||||
H0 = 1732584193,
|
||||
H1 = -271733879,
|
||||
H2 = -1732584194,
|
||||
H3 = 271733878,
|
||||
H4 = -1009589776;
|
||||
|
||||
// Padding
|
||||
m[l >> 5] |= 0x80 << (24 - l % 32);
|
||||
m[((l + 64 >>> 9) << 4) + 15] = l;
|
||||
|
||||
for (var i = 0; i < m.length; i += 16) {
|
||||
|
||||
var a = H0,
|
||||
b = H1,
|
||||
c = H2,
|
||||
d = H3,
|
||||
e = H4;
|
||||
|
||||
for (var j = 0; j < 80; j++) {
|
||||
|
||||
if (j < 16) w[j] = m[i + j];
|
||||
else {
|
||||
var n = w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16];
|
||||
w[j] = (n << 1) | (n >>> 31);
|
||||
}
|
||||
|
||||
var t = ((H0 << 5) | (H0 >>> 27)) + H4 + (w[j] >>> 0) + (
|
||||
j < 20 ? (H1 & H2 | ~H1 & H3) + 1518500249 :
|
||||
j < 40 ? (H1 ^ H2 ^ H3) + 1859775393 :
|
||||
j < 60 ? (H1 & H2 | H1 & H3 | H2 & H3) - 1894007588 :
|
||||
(H1 ^ H2 ^ H3) - 899497514);
|
||||
|
||||
H4 = H3;
|
||||
H3 = H2;
|
||||
H2 = (H1 << 30) | (H1 >>> 2);
|
||||
H1 = H0;
|
||||
H0 = t;
|
||||
|
||||
}
|
||||
|
||||
H0 += a;
|
||||
H1 += b;
|
||||
H2 += c;
|
||||
H3 += d;
|
||||
H4 += e;
|
||||
|
||||
}
|
||||
|
||||
return [H0, H1, H2, H3, H4];
|
||||
|
||||
};
|
||||
|
||||
// Package private blocksize
|
||||
SHA1._blocksize = 16;
|
||||
|
||||
})();
|
||||
256
vendor/github.com/camlistore/camlistore/clients/chrome/clip-it-good/background.html
generated
vendored
Normal file
@@ -0,0 +1,256 @@
|
||||
<html>
|
||||
<head>
|
||||
<script type="text/javascript" src="base64.js"></script>
|
||||
<script type="text/javascript" src="jquery-1.4.2.min.js"></script>
|
||||
<script type="text/javascript" src="chrome_ex_oauthsimple.js"></script>
|
||||
<script type="text/javascript" src="chrome_ex_oauth.js"></script>
|
||||
<script type="text/javascript" src="Crypto.js"></script>
|
||||
<script type="text/javascript" src="SHA1.js"></script>
|
||||
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
|
||||
var OAUTH = ChromeExOAuth.initBackgroundPage({
|
||||
'request_url' : 'https://www.google.com/accounts/OAuthGetRequestToken',
|
||||
'authorize_url' : 'https://www.google.com/accounts/OAuthAuthorizeToken',
|
||||
'access_url' : 'https://www.google.com/accounts/OAuthGetAccessToken',
|
||||
'consumer_key' : 'anonymous',
|
||||
'consumer_secret' : 'anonymous',
|
||||
'scope' : 'http://picasaweb.google.com/data/',
|
||||
'app_name' : 'Clip It Good: Chrome Extension'
|
||||
});
|
||||
|
||||
// Constants for various album types.
|
||||
var PICASA = 'picasa';
|
||||
var CAMLISTORE = 'camlistore';
|
||||
var ALBUM_TYPE_STRING = {
|
||||
picasa: 'Picasa Web Albums',
|
||||
camlistore: 'Camlistore'
|
||||
};
|
||||
|
||||
// Preferences
|
||||
var ALBUM_CONFIG = {}; // 'type' -> ('id' -> 'name')
|
||||
var ALBUM_OPTIONS = {}; // 'type' -> ('id' -> optionsDict)
|
||||
|
||||
function loadAlbumConfig() {
|
||||
var newAlbumConfig = localStorage.getItem('config:albums');
|
||||
if (newAlbumConfig) {
|
||||
ALBUM_CONFIG = $.parseJSON(newAlbumConfig);
|
||||
}
|
||||
|
||||
var newAlbumOptions = localStorage.getItem('config:albumOptions');
|
||||
if (newAlbumOptions) {
|
||||
ALBUM_OPTIONS = $.parseJSON(newAlbumOptions);
|
||||
}
|
||||
}
|
||||
|
||||
function saveAlbumConfig() {
|
||||
localStorage.setItem('config:albums', JSON.stringify(ALBUM_CONFIG));
|
||||
localStorage.setItem('config:albumOptions', JSON.stringify(ALBUM_OPTIONS));
|
||||
}
|
||||
|
||||
// Sort albums by name.
|
||||
function getSortedAlbums(albumIdNameDict) {
|
||||
var albumIdNameArray = [];
|
||||
$.each(albumIdNameDict, function(id, name) {
|
||||
albumIdNameArray.push({'id': id, 'name': name});
|
||||
});
|
||||
albumIdNameArray.sort(function(a, b) {
|
||||
if (a['name'] < b['name']) {
|
||||
return -1;
|
||||
} else if (a['name'] > b['name']) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
return albumIdNameArray;
|
||||
}
|
||||
|
||||
function setupMenus() {
|
||||
loadAlbumConfig();
|
||||
|
||||
chrome.contextMenus.removeAll(function() {
|
||||
$.each(ALBUM_CONFIG, function(albumType, albumIdNameDict) {
|
||||
chrome.contextMenus.create({
|
||||
title: ALBUM_TYPE_STRING[albumType],
|
||||
contexts: ['image']
|
||||
});
|
||||
chrome.contextMenus.create({
|
||||
type: 'separator',
|
||||
contexts: ['image']
|
||||
});
|
||||
|
||||
$.each(getSortedAlbums(albumIdNameDict), function(index, albumDict) {
|
||||
chrome.contextMenus.create({
|
||||
title: albumDict.name,
|
||||
contexts: ['image'],
|
||||
onclick: function(data, tab) {
|
||||
return handleMenuClick(
|
||||
albumType, albumDict.name, albumDict.id, data, tab);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Upload actions
|
||||
var ALBUM_TYPE_UPLOAD_FUNC = {
|
||||
|
||||
picasa: function(albumId, albumName, dataSrcUrl, dataBuffer, uploadDone) {
|
||||
var builder = new BlobBuilder();
|
||||
builder.append(dataBuffer);
|
||||
|
||||
function complete(resp, xhr) {
|
||||
if (!(xhr.status >= 200 && xhr.status <= 299)) {
|
||||
alert('Error: Response status = ' + xhr.status +
|
||||
', response body = "' + xhr.responseText + '"');
|
||||
} else {
|
||||
uploadDone();
|
||||
}
|
||||
}
|
||||
|
||||
OAUTH.authorize(function() {
|
||||
OAUTH.sendSignedRequest(
|
||||
'http://picasaweb.google.com/data/feed/api/' +
|
||||
'user/default/albumid/' + albumId,
|
||||
complete,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'image/png',
|
||||
'Slug': dataSrcUrl
|
||||
},
|
||||
parameters: {
|
||||
alt: 'json'
|
||||
},
|
||||
body: builder.getBlob('image/png')
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
camlistore: function(albumId, albumName, dataSrcUrl,
|
||||
dataBuffer, uploadDone) {
|
||||
var hash = Crypto.SHA1(new Uint8Array(dataBuffer, 0));
|
||||
var blobRef = 'sha1-' + hash;
|
||||
var options = ALBUM_OPTIONS[CAMLISTORE][albumId];
|
||||
|
||||
function doUpload(uploadUrl) {
|
||||
// XXX Use real random boundary.
|
||||
var boundary = 'randomboundaryXYZ';
|
||||
var contentType = 'multipart/form-data; boundary=' + boundary;
|
||||
|
||||
var header =
|
||||
'--' + boundary + '\r\n' +
|
||||
'Content-Type: application/octet-stream\r\n' +
|
||||
'Content-Disposition: form-data; name="' + blobRef +
|
||||
'"; filename="1"\r\n\r\n'
|
||||
var footer = '\r\n--' + boundary + '--\r\n';
|
||||
|
||||
var builder = new BlobBuilder();
|
||||
builder.append(header);
|
||||
builder.append(dataBuffer);
|
||||
builder.append(footer);
|
||||
var payload = builder.getBlob(contentType);
|
||||
|
||||
var uploadXhr = new XMLHttpRequest();
|
||||
uploadXhr.open('POST', uploadUrl, true,
|
||||
options.username, options.password);
|
||||
uploadXhr.onreadystatechange = function() {
|
||||
if (uploadXhr.readyState == XMLHttpRequest.DONE &&
|
||||
uploadXhr.status == 200) {
|
||||
// XXX Check for bad response format (not JSON).
|
||||
var responseJson = $.parseJSON(uploadXhr.responseText)
|
||||
|
||||
if (responseJson.received &&
|
||||
responseJson.received.length == 1 &&
|
||||
responseJson.received[0].blobRef == blobRef) {
|
||||
console.log('Successful upload: ' + blobRef);
|
||||
uploadDone();
|
||||
return;
|
||||
}
|
||||
|
||||
alert('Camlistore upload response did not verify blob "' +
|
||||
blobRef + '": ' + uploadXhr.responseText);
|
||||
}
|
||||
// XXX: Handle request errors
|
||||
}
|
||||
uploadXhr.setRequestHeader('Content-Type', contentType);
|
||||
uploadXhr.send(payload);
|
||||
}
|
||||
|
||||
var statXhr = new XMLHttpRequest();
|
||||
statXhr.open('POST', albumId + '/camli/stat', true,
|
||||
options.username, options.password);
|
||||
statXhr.onreadystatechange = function() {
|
||||
if (statXhr.readyState == XMLHttpRequest.DONE &&
|
||||
statXhr.status == 200) {
|
||||
// XXX Check for bad response format (not JSON).
|
||||
var responseJson = $.parseJSON(statXhr.responseText);
|
||||
|
||||
if (responseJson.stat &&
|
||||
responseJson.stat.length == 1 &&
|
||||
responseJson.stat[0].blobRef == blobRef) {
|
||||
console.log('Blob already present: ' + blobRef);
|
||||
uploadDone();
|
||||
return;
|
||||
}
|
||||
|
||||
var uploadUrl = responseJson.uploadUrl;
|
||||
if (!uploadUrl) {
|
||||
alert('Camlistore stat response missing "uploadUrl": ' +
|
||||
statXhr.responseText);
|
||||
return;
|
||||
}
|
||||
|
||||
doUpload(uploadUrl);
|
||||
}
|
||||
// XXX: Handle request errors
|
||||
}
|
||||
statXhr.setRequestHeader(
|
||||
'Content-Type', 'application/x-www-form-urlencoded');
|
||||
statXhr.send('camliversion=1&blob1=' + blobRef);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
function handleMenuClick(albumType, albumName, albumId, data, tab) {
|
||||
chrome.pageAction.setTitle({
|
||||
tabId: tab.id,
|
||||
title: 'Clip It Good: Uploading (' + data.srcUrl.substr(0, 100) + ')'
|
||||
});
|
||||
chrome.pageAction.show(tab.id);
|
||||
|
||||
var img = document.createElement('img');
|
||||
img.onload = function() {
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
canvas.getContext('2d').drawImage(img, 0, 0);
|
||||
|
||||
var dataUrl = canvas.toDataURL();
|
||||
var dataUrlAdjusted = dataUrl.replace('data:image/png;base64,', '');
|
||||
|
||||
var arrayBuffer = Base64.decode(dataUrlAdjusted).buffer;
|
||||
|
||||
function uploadDone() {
|
||||
chrome.pageAction.hide(tab.id);
|
||||
}
|
||||
|
||||
var uploadFunc = ALBUM_TYPE_UPLOAD_FUNC[albumType];
|
||||
uploadFunc(albumId, albumName, data.srcUrl, arrayBuffer, uploadDone);
|
||||
} // end onload
|
||||
|
||||
img.src = data.srcUrl;
|
||||
}
|
||||
|
||||
$(document).ready( function() {
|
||||
setupMenus();
|
||||
});
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
206
vendor/github.com/camlistore/camlistore/clients/chrome/clip-it-good/base64.js
generated
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
Copyright (c) 2008 Fred Palmer fred.palmer_at_gmail.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function StringBuffer()
|
||||
{
|
||||
this.buffer = [];
|
||||
}
|
||||
|
||||
StringBuffer.prototype.append = function append(string)
|
||||
{
|
||||
this.buffer.push(string);
|
||||
return this;
|
||||
};
|
||||
|
||||
StringBuffer.prototype.toString = function toString()
|
||||
{
|
||||
return this.buffer.join("");
|
||||
};
|
||||
|
||||
var Base64 =
|
||||
{
|
||||
codex : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
|
||||
|
||||
encode : function (input)
|
||||
{
|
||||
var output = new StringBuffer();
|
||||
|
||||
var enumerator = new Utf8EncodeEnumerator(input);
|
||||
while (enumerator.moveNext())
|
||||
{
|
||||
var chr1 = enumerator.current;
|
||||
|
||||
enumerator.moveNext();
|
||||
var chr2 = enumerator.current;
|
||||
|
||||
enumerator.moveNext();
|
||||
var chr3 = enumerator.current;
|
||||
|
||||
var enc1 = chr1 >> 2;
|
||||
var enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
|
||||
var enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
|
||||
var enc4 = chr3 & 63;
|
||||
|
||||
if (isNaN(chr2))
|
||||
{
|
||||
enc3 = enc4 = 64;
|
||||
}
|
||||
else if (isNaN(chr3))
|
||||
{
|
||||
enc4 = 64;
|
||||
}
|
||||
|
||||
output.append(this.codex.charAt(enc1) + this.codex.charAt(enc2) + this.codex.charAt(enc3) + this.codex.charAt(enc4));
|
||||
}
|
||||
|
||||
return output.toString();
|
||||
},
|
||||
|
||||
decode : function (input)
|
||||
{
|
||||
// TypedArray usage added by brett@haxor.com 11/27/2010
|
||||
var size = 0;
|
||||
var buffer = new ArrayBuffer(input.length);
|
||||
var output = new Uint8Array(buffer, 0);
|
||||
|
||||
var enumerator = new Base64DecodeEnumerator(input);
|
||||
while (enumerator.moveNext()) {
|
||||
output[size++] = enumerator.current;
|
||||
}
|
||||
|
||||
// There is nothing in the TypedArray spec to copy/subset a buffer,
|
||||
// so we have to do a copy to ensure that typedarray.buffer is the
|
||||
// correct length when passed to XmlHttpRequest methods, etc.
|
||||
var outputBuffer = new ArrayBuffer(size);
|
||||
var outputArray = new Uint8Array(outputBuffer, 0);
|
||||
for (var i = 0; i < size; i++) {
|
||||
outputArray[i] = output[i];
|
||||
}
|
||||
return outputArray;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function Utf8EncodeEnumerator(input)
|
||||
{
|
||||
this._input = input;
|
||||
this._index = -1;
|
||||
this._buffer = [];
|
||||
}
|
||||
|
||||
Utf8EncodeEnumerator.prototype =
|
||||
{
|
||||
current: Number.NaN,
|
||||
|
||||
moveNext: function()
|
||||
{
|
||||
if (this._buffer.length > 0)
|
||||
{
|
||||
this.current = this._buffer.shift();
|
||||
return true;
|
||||
}
|
||||
else if (this._index >= (this._input.length - 1))
|
||||
{
|
||||
this.current = Number.NaN;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
var charCode = this._input.charCodeAt(++this._index);
|
||||
|
||||
// "\r\n" -> "\n"
|
||||
//
|
||||
if ((charCode == 13) && (this._input.charCodeAt(this._index + 1) == 10))
|
||||
{
|
||||
charCode = 10;
|
||||
this._index += 2;
|
||||
}
|
||||
|
||||
if (charCode < 128)
|
||||
{
|
||||
this.current = charCode;
|
||||
}
|
||||
else if ((charCode > 127) && (charCode < 2048))
|
||||
{
|
||||
this.current = (charCode >> 6) | 192;
|
||||
this._buffer.push((charCode & 63) | 128);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.current = (charCode >> 12) | 224;
|
||||
this._buffer.push(((charCode >> 6) & 63) | 128);
|
||||
this._buffer.push((charCode & 63) | 128);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Base64DecodeEnumerator(input)
|
||||
{
|
||||
this._input = input;
|
||||
this._index = -1;
|
||||
this._buffer = [];
|
||||
}
|
||||
|
||||
Base64DecodeEnumerator.prototype =
|
||||
{
|
||||
current: 64,
|
||||
|
||||
moveNext: function()
|
||||
{
|
||||
if (this._buffer.length > 0)
|
||||
{
|
||||
this.current = this._buffer.shift();
|
||||
return true;
|
||||
}
|
||||
else if (this._index >= (this._input.length - 1))
|
||||
{
|
||||
this.current = 64;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
var enc1 = Base64.codex.indexOf(this._input.charAt(++this._index));
|
||||
var enc2 = Base64.codex.indexOf(this._input.charAt(++this._index));
|
||||
var enc3 = Base64.codex.indexOf(this._input.charAt(++this._index));
|
||||
var enc4 = Base64.codex.indexOf(this._input.charAt(++this._index));
|
||||
|
||||
var chr1 = (enc1 << 2) | (enc2 >> 4);
|
||||
var chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
|
||||
var chr3 = ((enc3 & 3) << 6) | enc4;
|
||||
|
||||
this.current = chr1;
|
||||
|
||||
if (enc3 != 64)
|
||||
this._buffer.push(chr2);
|
||||
|
||||
if (enc4 != 64)
|
||||
this._buffer.push(chr3);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
27
vendor/github.com/camlistore/camlistore/clients/chrome/clip-it-good/chrome_ex_oauth.html
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
* Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this
|
||||
* source code is governed by a BSD-style license that can be found in the
|
||||
* LICENSE file.
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>OAuth Redirect Page</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
font: 16px Arial;
|
||||
color: #333;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript" src="chrome_ex_oauthsimple.js"></script>
|
||||
<script type="text/javascript" src="chrome_ex_oauth.js"></script>
|
||||
<script type="text/javascript">
|
||||
function onLoad() {
|
||||
ChromeExOAuth.initCallbackPage();
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body onload="onLoad();">
|
||||
Redirecting...
|
||||
</body>
|
||||
</html>
|
||||
593
vendor/github.com/camlistore/camlistore/clients/chrome/clip-it-good/chrome_ex_oauth.js
generated
vendored
Normal file
@@ -0,0 +1,593 @@
|
||||
/**
|
||||
* Copyright (c) 2010 The Chromium Authors. All rights reserved. Use of this
|
||||
* source code is governed by a BSD-style license that can be found in the
|
||||
* LICENSE file.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Constructor - no need to invoke directly, call initBackgroundPage instead.
|
||||
* @constructor
|
||||
* @param {String} url_request_token The OAuth request token URL.
|
||||
* @param {String} url_auth_token The OAuth authorize token URL.
|
||||
* @param {String} url_access_token The OAuth access token URL.
|
||||
* @param {String} consumer_key The OAuth consumer key.
|
||||
* @param {String} consumer_secret The OAuth consumer secret.
|
||||
* @param {String} oauth_scope The OAuth scope parameter.
|
||||
* @param {Object} opt_args Optional arguments. Recognized parameters:
|
||||
* "app_name" {String} Name of the current application
|
||||
* "callback_page" {String} If you renamed chrome_ex_oauth.html, the name
|
||||
* this file was renamed to.
|
||||
*/
|
||||
function ChromeExOAuth(url_request_token, url_auth_token, url_access_token,
|
||||
consumer_key, consumer_secret, oauth_scope, opt_args) {
|
||||
this.url_request_token = url_request_token;
|
||||
this.url_auth_token = url_auth_token;
|
||||
this.url_access_token = url_access_token;
|
||||
this.consumer_key = consumer_key;
|
||||
this.consumer_secret = consumer_secret;
|
||||
this.oauth_scope = oauth_scope;
|
||||
this.app_name = opt_args && opt_args['app_name'] ||
|
||||
"ChromeExOAuth Library";
|
||||
this.key_token = "oauth_token";
|
||||
this.key_token_secret = "oauth_token_secret";
|
||||
this.callback_page = opt_args && opt_args['callback_page'] ||
|
||||
"chrome_ex_oauth.html";
|
||||
this.auth_params = {};
|
||||
if (opt_args && opt_args['auth_params']) {
|
||||
for (key in opt_args['auth_params']) {
|
||||
if (opt_args['auth_params'].hasOwnProperty(key)) {
|
||||
this.auth_params[key] = opt_args['auth_params'][key];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
* PUBLIC API METHODS
|
||||
* Call these from your background page.
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Initializes the OAuth helper from the background page. You must call this
|
||||
* before attempting to make any OAuth calls.
|
||||
* @param {Object} oauth_config Configuration parameters in a JavaScript object.
|
||||
* The following parameters are recognized:
|
||||
* "request_url" {String} OAuth request token URL.
|
||||
* "authorize_url" {String} OAuth authorize token URL.
|
||||
* "access_url" {String} OAuth access token URL.
|
||||
* "consumer_key" {String} OAuth consumer key.
|
||||
* "consumer_secret" {String} OAuth consumer secret.
|
||||
* "scope" {String} OAuth access scope.
|
||||
* "app_name" {String} Application name.
|
||||
* "auth_params" {Object} Additional parameters to pass to the
|
||||
* Authorization token URL. For an example, 'hd', 'hl', 'btmpl':
|
||||
* http://code.google.com/apis/accounts/docs/OAuth_ref.html#GetAuth
|
||||
* @return {ChromeExOAuth} An initialized ChromeExOAuth object.
|
||||
*/
|
||||
ChromeExOAuth.initBackgroundPage = function(oauth_config) {
|
||||
window.chromeExOAuthConfig = oauth_config;
|
||||
window.chromeExOAuth = ChromeExOAuth.fromConfig(oauth_config);
|
||||
window.chromeExOAuthRedirectStarted = false;
|
||||
window.chromeExOAuthRequestingAccess = false;
|
||||
|
||||
var url_match = chrome.extension.getURL(window.chromeExOAuth.callback_page);
|
||||
var tabs = {};
|
||||
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
|
||||
if (changeInfo.url &&
|
||||
changeInfo.url.substr(0, url_match.length) === url_match &&
|
||||
changeInfo.url != tabs[tabId] &&
|
||||
window.chromeExOAuthRequestingAccess == false) {
|
||||
chrome.tabs.create({ 'url' : changeInfo.url }, function(tab) {
|
||||
tabs[tab.id] = tab.url;
|
||||
chrome.tabs.remove(tabId);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return window.chromeExOAuth;
|
||||
};
|
||||
|
||||
/**
|
||||
* Authorizes the current user with the configued API. You must call this
|
||||
* before calling sendSignedRequest.
|
||||
* @param {Function} callback A function to call once an access token has
|
||||
* been obtained. This callback will be passed the following arguments:
|
||||
* token {String} The OAuth access token.
|
||||
* secret {String} The OAuth access token secret.
|
||||
*/
|
||||
ChromeExOAuth.prototype.authorize = function(callback) {
|
||||
if (this.hasToken()) {
|
||||
callback(this.getToken(), this.getTokenSecret());
|
||||
} else {
|
||||
window.chromeExOAuthOnAuthorize = function(token, secret) {
|
||||
callback(token, secret);
|
||||
};
|
||||
chrome.tabs.create({ 'url' :chrome.extension.getURL(this.callback_page) });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears any OAuth tokens stored for this configuration. Effectively a
|
||||
* "logout" of the configured OAuth API.
|
||||
*/
|
||||
ChromeExOAuth.prototype.clearTokens = function() {
|
||||
delete localStorage[this.key_token + encodeURI(this.oauth_scope)];
|
||||
delete localStorage[this.key_token_secret + encodeURI(this.oauth_scope)];
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns whether a token is currently stored for this configuration.
|
||||
* Effectively a check to see whether the current user is "logged in" to
|
||||
* the configured OAuth API.
|
||||
* @return {Boolean} True if an access token exists.
|
||||
*/
|
||||
ChromeExOAuth.prototype.hasToken = function() {
|
||||
return !!this.getToken();
|
||||
};
|
||||
|
||||
/**
|
||||
* Makes an OAuth-signed HTTP request with the currently authorized tokens.
|
||||
* @param {String} url The URL to send the request to. Querystring parameters
|
||||
* should be omitted.
|
||||
* @param {Function} callback A function to be called once the request is
|
||||
* completed. This callback will be passed the following arguments:
|
||||
* responseText {String} The text response.
|
||||
* xhr {XMLHttpRequest} The XMLHttpRequest object which was used to
|
||||
* send the request. Useful if you need to check response status
|
||||
* code, etc.
|
||||
* @param {Object} opt_params Additional parameters to configure the request.
|
||||
* The following parameters are accepted:
|
||||
* "method" {String} The HTTP method to use. Defaults to "GET".
|
||||
* "body" {String} A request body to send. Defaults to null.
|
||||
* "parameters" {Object} Query parameters to include in the request.
|
||||
* "headers" {Object} Additional headers to include in the request.
|
||||
*/
|
||||
ChromeExOAuth.prototype.sendSignedRequest = function(url, callback,
|
||||
opt_params) {
|
||||
var method = opt_params && opt_params['method'] || 'GET';
|
||||
var body = opt_params && opt_params['body'] || null;
|
||||
var params = opt_params && opt_params['parameters'] || {};
|
||||
var headers = opt_params && opt_params['headers'] || {};
|
||||
|
||||
var signedUrl = this.signURL(url, method, params);
|
||||
|
||||
ChromeExOAuth.sendRequest(method, signedUrl, headers, body, function (xhr) {
|
||||
if (xhr.readyState == 4) {
|
||||
callback(xhr.responseText, xhr);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds the required OAuth parameters to the given url and returns the
|
||||
* result. Useful if you need a signed url but don't want to make an XHR
|
||||
* request.
|
||||
* @param {String} method The http method to use.
|
||||
* @param {String} url The base url of the resource you are querying.
|
||||
* @param {Object} opt_params Query parameters to include in the request.
|
||||
* @return {String} The base url plus any query params plus any OAuth params.
|
||||
*/
|
||||
ChromeExOAuth.prototype.signURL = function(url, method, opt_params) {
|
||||
var token = this.getToken();
|
||||
var secret = this.getTokenSecret();
|
||||
if (!token || !secret) {
|
||||
throw new Error("No oauth token or token secret");
|
||||
}
|
||||
|
||||
var params = opt_params || {};
|
||||
|
||||
var result = OAuthSimple().sign({
|
||||
action : method,
|
||||
path : url,
|
||||
parameters : params,
|
||||
signatures: {
|
||||
consumer_key : this.consumer_key,
|
||||
shared_secret : this.consumer_secret,
|
||||
oauth_secret : secret,
|
||||
oauth_token: token
|
||||
}
|
||||
});
|
||||
|
||||
return result.signed_url;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates the Authorization header based on the oauth parameters.
|
||||
* @param {String} url The base url of the resource you are querying.
|
||||
* @param {Object} opt_params Query parameters to include in the request.
|
||||
* @return {String} An Authorization header containing the oauth_* params.
|
||||
*/
|
||||
ChromeExOAuth.prototype.getAuthorizationHeader = function(url, method,
|
||||
opt_params) {
|
||||
var token = this.getToken();
|
||||
var secret = this.getTokenSecret();
|
||||
if (!token || !secret) {
|
||||
throw new Error("No oauth token or token secret");
|
||||
}
|
||||
|
||||
var params = opt_params || {};
|
||||
|
||||
return OAuthSimple().getHeaderString({
|
||||
action: method,
|
||||
path : url,
|
||||
parameters : params,
|
||||
signatures: {
|
||||
consumer_key : this.consumer_key,
|
||||
shared_secret : this.consumer_secret,
|
||||
oauth_secret : secret,
|
||||
oauth_token: token
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
* PRIVATE API METHODS
|
||||
* Used by the library. There should be no need to call these methods directly.
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Creates a new ChromeExOAuth object from the supplied configuration object.
|
||||
* @param {Object} oauth_config Configuration parameters in a JavaScript object.
|
||||
* The following parameters are recognized:
|
||||
* "request_url" {String} OAuth request token URL.
|
||||
* "authorize_url" {String} OAuth authorize token URL.
|
||||
* "access_url" {String} OAuth access token URL.
|
||||
* "consumer_key" {String} OAuth consumer key.
|
||||
* "consumer_secret" {String} OAuth consumer secret.
|
||||
* "scope" {String} OAuth access scope.
|
||||
* "app_name" {String} Application name.
|
||||
* "auth_params" {Object} Additional parameters to pass to the
|
||||
* Authorization token URL. For an example, 'hd', 'hl', 'btmpl':
|
||||
* http://code.google.com/apis/accounts/docs/OAuth_ref.html#GetAuth
|
||||
* @return {ChromeExOAuth} An initialized ChromeExOAuth object.
|
||||
*/
|
||||
ChromeExOAuth.fromConfig = function(oauth_config) {
|
||||
return new ChromeExOAuth(
|
||||
oauth_config['request_url'],
|
||||
oauth_config['authorize_url'],
|
||||
oauth_config['access_url'],
|
||||
oauth_config['consumer_key'],
|
||||
oauth_config['consumer_secret'],
|
||||
oauth_config['scope'],
|
||||
{
|
||||
'app_name' : oauth_config['app_name'],
|
||||
'auth_params' : oauth_config['auth_params']
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes chrome_ex_oauth.html and redirects the page if needed to start
|
||||
* the OAuth flow. Once an access token is obtained, this function closes
|
||||
* chrome_ex_oauth.html.
|
||||
*/
|
||||
ChromeExOAuth.initCallbackPage = function() {
|
||||
var background_page = chrome.extension.getBackgroundPage();
|
||||
var oauth_config = background_page.chromeExOAuthConfig;
|
||||
var oauth = ChromeExOAuth.fromConfig(oauth_config);
|
||||
background_page.chromeExOAuthRedirectStarted = true;
|
||||
oauth.initOAuthFlow(function (token, secret) {
|
||||
background_page.chromeExOAuthOnAuthorize(token, secret);
|
||||
background_page.chromeExOAuthRedirectStarted = false;
|
||||
chrome.tabs.getSelected(null, function (tab) {
|
||||
chrome.tabs.remove(tab.id);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sends an HTTP request. Convenience wrapper for XMLHttpRequest calls.
|
||||
* @param {String} method The HTTP method to use.
|
||||
* @param {String} url The URL to send the request to.
|
||||
* @param {Object} headers Optional request headers in key/value format.
|
||||
* @param {String} body Optional body content.
|
||||
* @param {Function} callback Function to call when the XMLHttpRequest's
|
||||
* ready state changes. See documentation for XMLHttpRequest's
|
||||
* onreadystatechange handler for more information.
|
||||
*/
|
||||
ChromeExOAuth.sendRequest = function(method, url, headers, body, callback) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.onreadystatechange = function(data) {
|
||||
callback(xhr, data);
|
||||
}
|
||||
xhr.open(method, url, true);
|
||||
if (headers) {
|
||||
for (var header in headers) {
|
||||
if (headers.hasOwnProperty(header)) {
|
||||
xhr.setRequestHeader(header, headers[header]);
|
||||
}
|
||||
}
|
||||
}
|
||||
xhr.send(body);
|
||||
};
|
||||
|
||||
/**
|
||||
* Decodes a URL-encoded string into key/value pairs.
|
||||
* @param {String} encoded An URL-encoded string.
|
||||
* @return {Object} An object representing the decoded key/value pairs found
|
||||
* in the encoded string.
|
||||
*/
|
||||
ChromeExOAuth.formDecode = function(encoded) {
|
||||
var params = encoded.split("&");
|
||||
var decoded = {};
|
||||
for (var i = 0, param; param = params[i]; i++) {
|
||||
var keyval = param.split("=");
|
||||
if (keyval.length == 2) {
|
||||
var key = ChromeExOAuth.fromRfc3986(keyval[0]);
|
||||
var val = ChromeExOAuth.fromRfc3986(keyval[1]);
|
||||
decoded[key] = val;
|
||||
}
|
||||
}
|
||||
return decoded;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the current window's querystring decoded into key/value pairs.
|
||||
* @return {Object} A object representing any key/value pairs found in the
|
||||
* current window's querystring.
|
||||
*/
|
||||
ChromeExOAuth.getQueryStringParams = function() {
|
||||
var urlparts = window.location.href.split("?");
|
||||
if (urlparts.length >= 2) {
|
||||
var querystring = urlparts.slice(1).join("?");
|
||||
return ChromeExOAuth.formDecode(querystring);
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
/**
|
||||
* Binds a function call to a specific object. This function will also take
|
||||
* a variable number of additional arguments which will be prepended to the
|
||||
* arguments passed to the bound function when it is called.
|
||||
* @param {Function} func The function to bind.
|
||||
* @param {Object} obj The object to bind to the function's "this".
|
||||
* @return {Function} A closure that will call the bound function.
|
||||
*/
|
||||
ChromeExOAuth.bind = function(func, obj) {
|
||||
var newargs = Array.prototype.slice.call(arguments).slice(2);
|
||||
return function() {
|
||||
var combinedargs = newargs.concat(Array.prototype.slice.call(arguments));
|
||||
func.apply(obj, combinedargs);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Encodes a value according to the RFC3986 specification.
|
||||
* @param {String} val The string to encode.
|
||||
*/
|
||||
ChromeExOAuth.toRfc3986 = function(val){
|
||||
return encodeURIComponent(val)
|
||||
.replace(/\!/g, "%21")
|
||||
.replace(/\*/g, "%2A")
|
||||
.replace(/'/g, "%27")
|
||||
.replace(/\(/g, "%28")
|
||||
.replace(/\)/g, "%29");
|
||||
};
|
||||
|
||||
/**
|
||||
* Decodes a string that has been encoded according to RFC3986.
|
||||
* @param {String} val The string to decode.
|
||||
*/
|
||||
ChromeExOAuth.fromRfc3986 = function(val){
|
||||
var tmp = val
|
||||
.replace(/%21/g, "!")
|
||||
.replace(/%2A/g, "*")
|
||||
.replace(/%27/g, "'")
|
||||
.replace(/%28/g, "(")
|
||||
.replace(/%29/g, ")");
|
||||
return decodeURIComponent(tmp);
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a key/value parameter to the supplied URL.
|
||||
* @param {String} url An URL which may or may not contain querystring values.
|
||||
* @param {String} key A key
|
||||
* @param {String} value A value
|
||||
* @return {String} The URL with URL-encoded versions of the key and value
|
||||
* appended, prefixing them with "&" or "?" as needed.
|
||||
*/
|
||||
ChromeExOAuth.addURLParam = function(url, key, value) {
|
||||
var sep = (url.indexOf('?') >= 0) ? "&" : "?";
|
||||
return url + sep +
|
||||
ChromeExOAuth.toRfc3986(key) + "=" + ChromeExOAuth.toRfc3986(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores an OAuth token for the configured scope.
|
||||
* @param {String} token The token to store.
|
||||
*/
|
||||
ChromeExOAuth.prototype.setToken = function(token) {
|
||||
localStorage[this.key_token + encodeURI(this.oauth_scope)] = token;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves any stored token for the configured scope.
|
||||
* @return {String} The stored token.
|
||||
*/
|
||||
ChromeExOAuth.prototype.getToken = function() {
|
||||
return localStorage[this.key_token + encodeURI(this.oauth_scope)];
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores an OAuth token secret for the configured scope.
|
||||
* @param {String} secret The secret to store.
|
||||
*/
|
||||
ChromeExOAuth.prototype.setTokenSecret = function(secret) {
|
||||
localStorage[this.key_token_secret + encodeURI(this.oauth_scope)] = secret;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves any stored secret for the configured scope.
|
||||
* @return {String} The stored secret.
|
||||
*/
|
||||
ChromeExOAuth.prototype.getTokenSecret = function() {
|
||||
return localStorage[this.key_token_secret + encodeURI(this.oauth_scope)];
|
||||
};
|
||||
|
||||
/**
|
||||
* Starts an OAuth authorization flow for the current page. If a token exists,
|
||||
* no redirect is needed and the supplied callback is called immediately.
|
||||
* If this method detects that a redirect has finished, it grabs the
|
||||
* appropriate OAuth parameters from the URL and attempts to retrieve an
|
||||
* access token. If no token exists and no redirect has happened, then
|
||||
* an access token is requested and the page is ultimately redirected.
|
||||
* @param {Function} callback The function to call once the flow has finished.
|
||||
* This callback will be passed the following arguments:
|
||||
* token {String} The OAuth access token.
|
||||
* secret {String} The OAuth access token secret.
|
||||
*/
|
||||
ChromeExOAuth.prototype.initOAuthFlow = function(callback) {
|
||||
if (!this.hasToken()) {
|
||||
var params = ChromeExOAuth.getQueryStringParams();
|
||||
if (params['chromeexoauthcallback'] == 'true') {
|
||||
var oauth_token = params['oauth_token'];
|
||||
var oauth_verifier = params['oauth_verifier']
|
||||
this.getAccessToken(oauth_token, oauth_verifier, callback);
|
||||
} else {
|
||||
var request_params = {
|
||||
'url_callback_param' : 'chromeexoauthcallback'
|
||||
}
|
||||
this.getRequestToken(function(url) {
|
||||
window.location.href = url;
|
||||
}, request_params);
|
||||
}
|
||||
} else {
|
||||
callback(this.getToken(), this.getTokenSecret());
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Requests an OAuth request token.
|
||||
* @param {Function} callback Function to call once the authorize URL is
|
||||
* calculated. This callback will be passed the following arguments:
|
||||
* url {String} The URL the user must be redirected to in order to
|
||||
* approve the token.
|
||||
* @param {Object} opt_args Optional arguments. The following parameters
|
||||
* are accepted:
|
||||
* "url_callback" {String} The URL the OAuth provider will redirect to.
|
||||
* "url_callback_param" {String} A parameter to include in the callback
|
||||
* URL in order to indicate to this library that a redirect has
|
||||
* taken place.
|
||||
*/
|
||||
ChromeExOAuth.prototype.getRequestToken = function(callback, opt_args) {
|
||||
if (typeof callback !== "function") {
|
||||
throw new Error("Specified callback must be a function.");
|
||||
}
|
||||
var url = opt_args && opt_args['url_callback'] ||
|
||||
window && window.top && window.top.location &&
|
||||
window.top.location.href;
|
||||
|
||||
var url_param = opt_args && opt_args['url_callback_param'] ||
|
||||
"chromeexoauthcallback";
|
||||
var url_callback = ChromeExOAuth.addURLParam(url, url_param, "true");
|
||||
|
||||
var result = OAuthSimple().sign({
|
||||
path : this.url_request_token,
|
||||
parameters: {
|
||||
"xoauth_displayname" : this.app_name,
|
||||
"scope" : this.oauth_scope,
|
||||
"oauth_callback" : url_callback
|
||||
},
|
||||
signatures: {
|
||||
consumer_key : this.consumer_key,
|
||||
shared_secret : this.consumer_secret
|
||||
}
|
||||
});
|
||||
var onToken = ChromeExOAuth.bind(this.onRequestToken, this, callback);
|
||||
ChromeExOAuth.sendRequest("GET", result.signed_url, null, null, onToken);
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when a request token has been returned. Stores the request token
|
||||
* secret for later use and sends the authorization url to the supplied
|
||||
* callback (for redirecting the user).
|
||||
* @param {Function} callback Function to call once the authorize URL is
|
||||
* calculated. This callback will be passed the following arguments:
|
||||
* url {String} The URL the user must be redirected to in order to
|
||||
* approve the token.
|
||||
* @param {XMLHttpRequest} xhr The XMLHttpRequest object used to fetch the
|
||||
* request token.
|
||||
*/
|
||||
ChromeExOAuth.prototype.onRequestToken = function(callback, xhr) {
|
||||
if (xhr.readyState == 4) {
|
||||
if (xhr.status == 200) {
|
||||
var params = ChromeExOAuth.formDecode(xhr.responseText);
|
||||
var token = params['oauth_token'];
|
||||
this.setTokenSecret(params['oauth_token_secret']);
|
||||
var url = ChromeExOAuth.addURLParam(this.url_auth_token,
|
||||
"oauth_token", token);
|
||||
for (var key in this.auth_params) {
|
||||
if (this.auth_params.hasOwnProperty(key)) {
|
||||
url = ChromeExOAuth.addURLParam(url, key, this.auth_params[key]);
|
||||
}
|
||||
}
|
||||
callback(url);
|
||||
} else {
|
||||
throw new Error("Fetching request token failed. Status " + xhr.status);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Requests an OAuth access token.
|
||||
* @param {String} oauth_token The OAuth request token.
|
||||
* @param {String} oauth_verifier The OAuth token verifier.
|
||||
* @param {Function} callback The function to call once the token is obtained.
|
||||
* This callback will be passed the following arguments:
|
||||
* token {String} The OAuth access token.
|
||||
* secret {String} The OAuth access token secret.
|
||||
*/
|
||||
ChromeExOAuth.prototype.getAccessToken = function(oauth_token, oauth_verifier,
|
||||
callback) {
|
||||
if (typeof callback !== "function") {
|
||||
throw new Error("Specified callback must be a function.");
|
||||
}
|
||||
var bg = chrome.extension.getBackgroundPage();
|
||||
if (bg.chromeExOAuthRequestingAccess == false) {
|
||||
bg.chromeExOAuthRequestingAccess = true;
|
||||
|
||||
var result = OAuthSimple().sign({
|
||||
path : this.url_access_token,
|
||||
parameters: {
|
||||
"oauth_token" : oauth_token,
|
||||
"oauth_verifier" : oauth_verifier
|
||||
},
|
||||
signatures: {
|
||||
consumer_key : this.consumer_key,
|
||||
shared_secret : this.consumer_secret,
|
||||
oauth_secret : this.getTokenSecret(this.oauth_scope)
|
||||
}
|
||||
});
|
||||
|
||||
var onToken = ChromeExOAuth.bind(this.onAccessToken, this, callback);
|
||||
ChromeExOAuth.sendRequest("GET", result.signed_url, null, null, onToken);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when an access token has been returned. Stores the access token and
|
||||
* access token secret for later use and sends them to the supplied callback.
|
||||
* @param {Function} callback The function to call once the token is obtained.
|
||||
* This callback will be passed the following arguments:
|
||||
* token {String} The OAuth access token.
|
||||
* secret {String} The OAuth access token secret.
|
||||
* @param {XMLHttpRequest} xhr The XMLHttpRequest object used to fetch the
|
||||
* access token.
|
||||
*/
|
||||
ChromeExOAuth.prototype.onAccessToken = function(callback, xhr) {
|
||||
if (xhr.readyState == 4) {
|
||||
var bg = chrome.extension.getBackgroundPage();
|
||||
if (xhr.status == 200) {
|
||||
var params = ChromeExOAuth.formDecode(xhr.responseText);
|
||||
var token = params["oauth_token"];
|
||||
var secret = params["oauth_token_secret"];
|
||||
this.setToken(token);
|
||||
this.setTokenSecret(secret);
|
||||
bg.chromeExOAuthRequestingAccess = false;
|
||||
callback(token, secret);
|
||||
} else {
|
||||
bg.chromeExOAuthRequestingAccess = false;
|
||||
throw new Error("Fetching access token failed with status " + xhr.status);
|
||||
}
|
||||
}
|
||||
};
|
||||
458
vendor/github.com/camlistore/camlistore/clients/chrome/clip-it-good/chrome_ex_oauthsimple.js
generated
vendored
Normal file
@@ -0,0 +1,458 @@
|
||||
/* OAuthSimple
|
||||
* A simpler version of OAuth
|
||||
*
|
||||
* author: jr conlin
|
||||
* mail: src@anticipatr.com
|
||||
* copyright: unitedHeroes.net
|
||||
* version: 1.0
|
||||
* url: http://unitedHeroes.net/OAuthSimple
|
||||
*
|
||||
* Copyright (c) 2009, unitedHeroes.net
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the unitedHeroes.net nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY UNITEDHEROES.NET ''AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL UNITEDHEROES.NET BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
var OAuthSimple;
|
||||
|
||||
if (OAuthSimple === undefined)
|
||||
{
|
||||
/* Simple OAuth
|
||||
*
|
||||
* This class only builds the OAuth elements, it does not do the actual
|
||||
* transmission or reception of the tokens. It does not validate elements
|
||||
* of the token. It is for client use only.
|
||||
*
|
||||
* api_key is the API key, also known as the OAuth consumer key
|
||||
* shared_secret is the shared secret (duh).
|
||||
*
|
||||
* Both the api_key and shared_secret are generally provided by the site
|
||||
* offering OAuth services. You need to specify them at object creation
|
||||
* because nobody <explative>ing uses OAuth without that minimal set of
|
||||
* signatures.
|
||||
*
|
||||
* If you want to use the higher order security that comes from the
|
||||
* OAuth token (sorry, I don't provide the functions to fetch that because
|
||||
* sites aren't horribly consistent about how they offer that), you need to
|
||||
* pass those in either with .setTokensAndSecrets() or as an argument to the
|
||||
* .sign() or .getHeaderString() functions.
|
||||
*
|
||||
* Example:
|
||||
<code>
|
||||
var oauthObject = OAuthSimple().sign({path:'http://example.com/rest/',
|
||||
parameters: 'foo=bar&gorp=banana',
|
||||
signatures:{
|
||||
api_key:'12345abcd',
|
||||
shared_secret:'xyz-5309'
|
||||
}});
|
||||
document.getElementById('someLink').href=oauthObject.signed_url;
|
||||
</code>
|
||||
*
|
||||
* that will sign as a "GET" using "SHA1-MAC" the url. If you need more than
|
||||
* that, read on, McDuff.
|
||||
*/
|
||||
|
||||
/** OAuthSimple creator
|
||||
*
|
||||
* Create an instance of OAuthSimple
|
||||
*
|
||||
* @param api_key {string} The API Key (sometimes referred to as the consumer key) This value is usually supplied by the site you wish to use.
|
||||
* @param shared_secret (string) The shared secret. This value is also usually provided by the site you wish to use.
|
||||
*/
|
||||
OAuthSimple = function (consumer_key,shared_secret)
|
||||
{
|
||||
/* if (api_key == undefined)
|
||||
throw("Missing argument: api_key (oauth_consumer_key) for OAuthSimple. This is usually provided by the hosting site.");
|
||||
if (shared_secret == undefined)
|
||||
throw("Missing argument: shared_secret (shared secret) for OAuthSimple. This is usually provided by the hosting site.");
|
||||
*/ this._secrets={};
|
||||
this._parameters={};
|
||||
|
||||
// General configuration options.
|
||||
if (consumer_key !== undefined) {
|
||||
this._secrets['consumer_key'] = consumer_key;
|
||||
}
|
||||
if (shared_secret !== undefined) {
|
||||
this._secrets['shared_secret'] = shared_secret;
|
||||
}
|
||||
this._default_signature_method= "HMAC-SHA1";
|
||||
this._action = "GET";
|
||||
this._nonce_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
|
||||
this.reset = function() {
|
||||
this._parameters={};
|
||||
this._path=undefined;
|
||||
return this;
|
||||
};
|
||||
|
||||
/** set the parameters either from a hash or a string
|
||||
*
|
||||
* @param {string,object} List of parameters for the call, this can either be a URI string (e.g. "foo=bar&gorp=banana" or an object/hash)
|
||||
*/
|
||||
this.setParameters = function (parameters) {
|
||||
if (parameters === undefined) {
|
||||
parameters = {};
|
||||
}
|
||||
if (typeof(parameters) == 'string') {
|
||||
parameters=this._parseParameterString(parameters);
|
||||
}
|
||||
this._parameters = parameters;
|
||||
if (this._parameters['oauth_nonce'] === undefined) {
|
||||
this._getNonce();
|
||||
}
|
||||
if (this._parameters['oauth_timestamp'] === undefined) {
|
||||
this._getTimestamp();
|
||||
}
|
||||
if (this._parameters['oauth_method'] === undefined) {
|
||||
this.setSignatureMethod();
|
||||
}
|
||||
if (this._parameters['oauth_consumer_key'] === undefined) {
|
||||
this._getApiKey();
|
||||
}
|
||||
if(this._parameters['oauth_token'] === undefined) {
|
||||
this._getAccessToken();
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/** convienence method for setParameters
|
||||
*
|
||||
* @param parameters {string,object} See .setParameters
|
||||
*/
|
||||
this.setQueryString = function (parameters) {
|
||||
return this.setParameters(parameters);
|
||||
};
|
||||
|
||||
/** Set the target URL (does not include the parameters)
|
||||
*
|
||||
* @param path {string} the fully qualified URI (excluding query arguments) (e.g "http://example.org/foo")
|
||||
*/
|
||||
this.setURL = function (path) {
|
||||
if (path == '') {
|
||||
throw ('No path specified for OAuthSimple.setURL');
|
||||
}
|
||||
this._path = path;
|
||||
return this;
|
||||
};
|
||||
|
||||
/** convienence method for setURL
|
||||
*
|
||||
* @param path {string} see .setURL
|
||||
*/
|
||||
this.setPath = function(path){
|
||||
return this.setURL(path);
|
||||
};
|
||||
|
||||
/** set the "action" for the url, (e.g. GET,POST, DELETE, etc.)
|
||||
*
|
||||
* @param action {string} HTTP Action word.
|
||||
*/
|
||||
this.setAction = function(action) {
|
||||
if (action === undefined) {
|
||||
action="GET";
|
||||
}
|
||||
action = action.toUpperCase();
|
||||
if (action.match('[^A-Z]')) {
|
||||
throw ('Invalid action specified for OAuthSimple.setAction');
|
||||
}
|
||||
this._action = action;
|
||||
return this;
|
||||
};
|
||||
|
||||
/** set the signatures (as well as validate the ones you have)
|
||||
*
|
||||
* @param signatures {object} object/hash of the token/signature pairs {api_key:, shared_secret:, oauth_token: oauth_secret:}
|
||||
*/
|
||||
this.setTokensAndSecrets = function(signatures) {
|
||||
if (signatures)
|
||||
{
|
||||
for (var i in signatures) {
|
||||
this._secrets[i] = signatures[i];
|
||||
}
|
||||
}
|
||||
// Aliases
|
||||
if (this._secrets['api_key']) {
|
||||
this._secrets.consumer_key = this._secrets.api_key;
|
||||
}
|
||||
if (this._secrets['access_token']) {
|
||||
this._secrets.oauth_token = this._secrets.access_token;
|
||||
}
|
||||
if (this._secrets['access_secret']) {
|
||||
this._secrets.oauth_secret = this._secrets.access_secret;
|
||||
}
|
||||
// Gauntlet
|
||||
if (this._secrets.consumer_key === undefined) {
|
||||
throw('Missing required consumer_key in OAuthSimple.setTokensAndSecrets');
|
||||
}
|
||||
if (this._secrets.shared_secret === undefined) {
|
||||
throw('Missing required shared_secret in OAuthSimple.setTokensAndSecrets');
|
||||
}
|
||||
if ((this._secrets.oauth_token !== undefined) && (this._secrets.oauth_secret === undefined)) {
|
||||
throw('Missing oauth_secret for supplied oauth_token in OAuthSimple.setTokensAndSecrets');
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/** set the signature method (currently only Plaintext or SHA-MAC1)
|
||||
*
|
||||
* @param method {string} Method of signing the transaction (only PLAINTEXT and SHA-MAC1 allowed for now)
|
||||
*/
|
||||
this.setSignatureMethod = function(method) {
|
||||
if (method === undefined) {
|
||||
method = this._default_signature_method;
|
||||
}
|
||||
//TODO: accept things other than PlainText or SHA-MAC1
|
||||
if (method.toUpperCase().match(/(PLAINTEXT|HMAC-SHA1)/) === undefined) {
|
||||
throw ('Unknown signing method specified for OAuthSimple.setSignatureMethod');
|
||||
}
|
||||
this._parameters['oauth_signature_method']= method.toUpperCase();
|
||||
return this;
|
||||
};
|
||||
|
||||
/** sign the request
|
||||
*
|
||||
* note: all arguments are optional, provided you've set them using the
|
||||
* other helper functions.
|
||||
*
|
||||
* @param args {object} hash of arguments for the call
|
||||
* {action:, path:, parameters:, method:, signatures:}
|
||||
* all arguments are optional.
|
||||
*/
|
||||
this.sign = function (args) {
|
||||
if (args === undefined) {
|
||||
args = {};
|
||||
}
|
||||
// Set any given parameters
|
||||
if(args['action'] !== undefined) {
|
||||
this.setAction(args['action']);
|
||||
}
|
||||
if (args['path'] !== undefined) {
|
||||
this.setPath(args['path']);
|
||||
}
|
||||
if (args['method'] !== undefined) {
|
||||
this.setSignatureMethod(args['method']);
|
||||
}
|
||||
this.setTokensAndSecrets(args['signatures']);
|
||||
if (args['parameters'] !== undefined){
|
||||
this.setParameters(args['parameters']);
|
||||
}
|
||||
// check the parameters
|
||||
var normParams = this._normalizedParameters();
|
||||
this._parameters['oauth_signature']=this._generateSignature(normParams);
|
||||
return {
|
||||
parameters: this._parameters,
|
||||
signature: this._oauthEscape(this._parameters['oauth_signature']),
|
||||
signed_url: this._path + '?' + this._normalizedParameters(),
|
||||
header: this.getHeaderString()
|
||||
};
|
||||
};
|
||||
|
||||
/** Return a formatted "header" string
|
||||
*
|
||||
* NOTE: This doesn't set the "Authorization: " prefix, which is required.
|
||||
* I don't set it because various set header functions prefer different
|
||||
* ways to do that.
|
||||
*
|
||||
* @param args {object} see .sign
|
||||
*/
|
||||
this.getHeaderString = function(args) {
|
||||
if (this._parameters['oauth_signature'] === undefined) {
|
||||
this.sign(args);
|
||||
}
|
||||
|
||||
var result = 'OAuth ';
|
||||
for (var pName in this._parameters)
|
||||
{
|
||||
if (!pName.match(/^oauth/)) {
|
||||
continue;
|
||||
}
|
||||
if ((this._parameters[pName]) instanceof Array)
|
||||
{
|
||||
var pLength = this._parameters[pName].length;
|
||||
for (var j=0;j<pLength;j++)
|
||||
{
|
||||
result += pName +'="'+this._oauthEscape(this._parameters[pName][j])+'" ';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result += pName + '="'+this._oauthEscape(this._parameters[pName])+'" ';
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
// Start Private Methods.
|
||||
|
||||
/** convert the parameter string into a hash of objects.
|
||||
*
|
||||
*/
|
||||
this._parseParameterString = function(paramString){
|
||||
var elements = paramString.split('&');
|
||||
var result={};
|
||||
for(var element=elements.shift();element;element=elements.shift())
|
||||
{
|
||||
var keyToken=element.split('=');
|
||||
var value='';
|
||||
if (keyToken[1]) {
|
||||
value=decodeURIComponent(keyToken[1]);
|
||||
}
|
||||
if(result[keyToken[0]]){
|
||||
if (!(result[keyToken[0]] instanceof Array))
|
||||
{
|
||||
result[keyToken[0]] = Array(result[keyToken[0]],value);
|
||||
}
|
||||
else
|
||||
{
|
||||
result[keyToken[0]].push(value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result[keyToken[0]]=value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
this._oauthEscape = function(string) {
|
||||
if (string === undefined) {
|
||||
return "";
|
||||
}
|
||||
if (string instanceof Array)
|
||||
{
|
||||
throw('Array passed to _oauthEscape');
|
||||
}
|
||||
return encodeURIComponent(string).replace(/\!/g, "%21").
|
||||
replace(/\*/g, "%2A").
|
||||
replace(/'/g, "%27").
|
||||
replace(/\(/g, "%28").
|
||||
replace(/\)/g, "%29");
|
||||
};
|
||||
|
||||
this._getNonce = function (length) {
|
||||
if (length === undefined) {
|
||||
length=5;
|
||||
}
|
||||
var result = "";
|
||||
var cLength = this._nonce_chars.length;
|
||||
for (var i = 0; i < length;i++) {
|
||||
var rnum = Math.floor(Math.random() *cLength);
|
||||
result += this._nonce_chars.substring(rnum,rnum+1);
|
||||
}
|
||||
this._parameters['oauth_nonce']=result;
|
||||
return result;
|
||||
};
|
||||
|
||||
this._getApiKey = function() {
|
||||
if (this._secrets.consumer_key === undefined) {
|
||||
throw('No consumer_key set for OAuthSimple.');
|
||||
}
|
||||
this._parameters['oauth_consumer_key']=this._secrets.consumer_key;
|
||||
return this._parameters.oauth_consumer_key;
|
||||
};
|
||||
|
||||
this._getAccessToken = function() {
|
||||
if (this._secrets['oauth_secret'] === undefined) {
|
||||
return '';
|
||||
}
|
||||
if (this._secrets['oauth_token'] === undefined) {
|
||||
throw('No oauth_token (access_token) set for OAuthSimple.');
|
||||
}
|
||||
this._parameters['oauth_token'] = this._secrets.oauth_token;
|
||||
return this._parameters.oauth_token;
|
||||
};
|
||||
|
||||
this._getTimestamp = function() {
|
||||
var d = new Date();
|
||||
var ts = Math.floor(d.getTime()/1000);
|
||||
this._parameters['oauth_timestamp'] = ts;
|
||||
return ts;
|
||||
};
|
||||
|
||||
this.b64_hmac_sha1 = function(k,d,_p,_z){
|
||||
// heavily optimized and compressed version of http://pajhome.org.uk/crypt/md5/sha1.js
|
||||
// _p = b64pad, _z = character size; not used here but I left them available just in case
|
||||
if(!_p){_p='=';}if(!_z){_z=8;}function _f(t,b,c,d){if(t<20){return(b&c)|((~b)&d);}if(t<40){return b^c^d;}if(t<60){return(b&c)|(b&d)|(c&d);}return b^c^d;}function _k(t){return(t<20)?1518500249:(t<40)?1859775393:(t<60)?-1894007588:-899497514;}function _s(x,y){var l=(x&0xFFFF)+(y&0xFFFF),m=(x>>16)+(y>>16)+(l>>16);return(m<<16)|(l&0xFFFF);}function _r(n,c){return(n<<c)|(n>>>(32-c));}function _c(x,l){x[l>>5]|=0x80<<(24-l%32);x[((l+64>>9)<<4)+15]=l;var w=[80],a=1732584193,b=-271733879,c=-1732584194,d=271733878,e=-1009589776;for(var i=0;i<x.length;i+=16){var o=a,p=b,q=c,r=d,s=e;for(var j=0;j<80;j++){if(j<16){w[j]=x[i+j];}else{w[j]=_r(w[j-3]^w[j-8]^w[j-14]^w[j-16],1);}var t=_s(_s(_r(a,5),_f(j,b,c,d)),_s(_s(e,w[j]),_k(j)));e=d;d=c;c=_r(b,30);b=a;a=t;}a=_s(a,o);b=_s(b,p);c=_s(c,q);d=_s(d,r);e=_s(e,s);}return[a,b,c,d,e];}function _b(s){var b=[],m=(1<<_z)-1;for(var i=0;i<s.length*_z;i+=_z){b[i>>5]|=(s.charCodeAt(i/8)&m)<<(32-_z-i%32);}return b;}function _h(k,d){var b=_b(k);if(b.length>16){b=_c(b,k.length*_z);}var p=[16],o=[16];for(var i=0;i<16;i++){p[i]=b[i]^0x36363636;o[i]=b[i]^0x5C5C5C5C;}var h=_c(p.concat(_b(d)),512+d.length*_z);return _c(o.concat(h),512+160);}function _n(b){var t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",s='';for(var i=0;i<b.length*4;i+=3){var r=(((b[i>>2]>>8*(3-i%4))&0xFF)<<16)|(((b[i+1>>2]>>8*(3-(i+1)%4))&0xFF)<<8)|((b[i+2>>2]>>8*(3-(i+2)%4))&0xFF);for(var j=0;j<4;j++){if(i*8+j*6>b.length*32){s+=_p;}else{s+=t.charAt((r>>6*(3-j))&0x3F);}}}return s;}function _x(k,d){return _n(_h(k,d));}return _x(k,d);
|
||||
}
|
||||
|
||||
|
||||
this._normalizedParameters = function() {
|
||||
var elements = new Array();
|
||||
var paramNames = [];
|
||||
var ra =0;
|
||||
for (var paramName in this._parameters)
|
||||
{
|
||||
if (ra++ > 1000) {
|
||||
throw('runaway 1');
|
||||
}
|
||||
paramNames.unshift(paramName);
|
||||
}
|
||||
paramNames = paramNames.sort();
|
||||
pLen = paramNames.length;
|
||||
for (var i=0;i<pLen; i++)
|
||||
{
|
||||
paramName=paramNames[i];
|
||||
//skip secrets.
|
||||
if (paramName.match(/\w+_secret/)) {
|
||||
continue;
|
||||
}
|
||||
if (this._parameters[paramName] instanceof Array)
|
||||
{
|
||||
var sorted = this._parameters[paramName].sort();
|
||||
var spLen = sorted.length;
|
||||
for (var j = 0;j<spLen;j++){
|
||||
if (ra++ > 1000) {
|
||||
throw('runaway 1');
|
||||
}
|
||||
elements.push(this._oauthEscape(paramName) + '=' +
|
||||
this._oauthEscape(sorted[j]));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
elements.push(this._oauthEscape(paramName) + '=' +
|
||||
this._oauthEscape(this._parameters[paramName]));
|
||||
}
|
||||
return elements.join('&');
|
||||
};
|
||||
|
||||
this._generateSignature = function() {
|
||||
|
||||
var secretKey = this._oauthEscape(this._secrets.shared_secret)+'&'+
|
||||
this._oauthEscape(this._secrets.oauth_secret);
|
||||
if (this._parameters['oauth_signature_method'] == 'PLAINTEXT')
|
||||
{
|
||||
return secretKey;
|
||||
}
|
||||
if (this._parameters['oauth_signature_method'] == 'HMAC-SHA1')
|
||||
{
|
||||
var sigString = this._oauthEscape(this._action)+'&'+this._oauthEscape(this._path)+'&'+this._oauthEscape(this._normalizedParameters());
|
||||
return this.b64_hmac_sha1(secretKey,sigString);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
return this;
|
||||
};
|
||||
}
|
||||
BIN
vendor/github.com/camlistore/camlistore/clients/chrome/clip-it-good/icon128.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
vendor/github.com/camlistore/camlistore/clients/chrome/clip-it-good/icon19.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 519 B |
BIN
vendor/github.com/camlistore/camlistore/clients/chrome/clip-it-good/icon48.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
vendor/github.com/camlistore/camlistore/clients/chrome/clip-it-good/images/ui-bg_diagonals-thick_18_b81900_40x40.png
generated
vendored
Executable file
|
After Width: | Height: | Size: 384 B |
BIN
vendor/github.com/camlistore/camlistore/clients/chrome/clip-it-good/images/ui-bg_diagonals-thick_20_666666_40x40.png
generated
vendored
Executable file
|
After Width: | Height: | Size: 251 B |
BIN
vendor/github.com/camlistore/camlistore/clients/chrome/clip-it-good/images/ui-bg_flat_10_000000_40x100.png
generated
vendored
Executable file
|
After Width: | Height: | Size: 178 B |
BIN
vendor/github.com/camlistore/camlistore/clients/chrome/clip-it-good/images/ui-bg_glass_100_f6f6f6_1x400.png
generated
vendored
Executable file
|
After Width: | Height: | Size: 104 B |
BIN
vendor/github.com/camlistore/camlistore/clients/chrome/clip-it-good/images/ui-bg_glass_100_fdf5ce_1x400.png
generated
vendored
Executable file
|
After Width: | Height: | Size: 125 B |
BIN
vendor/github.com/camlistore/camlistore/clients/chrome/clip-it-good/images/ui-bg_glass_65_ffffff_1x400.png
generated
vendored
Executable file
|
After Width: | Height: | Size: 105 B |