4 [Coquelicot] is written in Ruby and should be quite easy to improve for
5 anyone a little bit familiar with the Sinatra web framework. It is
6 mostly written using Behaviour Driven Development, making the test suite
7 a fine net to hack in confidence. So please go ahead!
9 [Coquelicot]: https://coquelicot.potager.org/
11 Setup a work environment
12 ------------------------
14 As Coquelicot uses Bundle, the first step to work on Coquelicot
15 after cloning its Git repository is installing the proper dependencies by
23 Coquelicot test suite is written using RSpec. Running the test suite is
24 just a matter of typing:
28 Running a test server can be done with:
30 bundle exec coquelicot start --no-daemon
32 To update the translation source files, use:
34 bundle exec rake gettext:po:update
36 This will update `po/coquelicot.pot` and merge the new strings in the various
37 `po/*/coquelicot.po` files.
39 Authentication mechanisms
40 -------------------------
42 The authentication part of Coquelicot has been made modular. Adding a
43 new authentication mechanism should be fairly straightforward.
45 A new authentication mechanism needs to provide the following 3 files,
46 with the following responsabilities:
48 * `lib/coquelicot/auth/<METHOD>.rb`:
50 A class implementing the actual authentication. This class must
51 implement an `authenticate` method. It will receive the form fields
52 as usual (params). This method must return true if upload
55 * `public/javascripts/coquelicot.auth.<METHOD>.js:`
57 This file must define 'authentication' as an object with the
60 - `getData()`: return an object of all the necessary data
61 to authenticate on the app side. Keys must have the same name
62 as the input fields used to authenticate without JavaScript.
63 - `focus()`: set the focus on the first authentication form field.
64 - (optional) `handleSuccess()`: arbitrary action upon successful
65 authentication. This is called after the livebox with
66 authentication fields is closed.
67 - (optional) `handleReject()`: arbitrary action when access
68 is denied. One can reset authentication fields after a failed
70 - (optional) `handleFailure()`: arbitrary action when there was
71 a problem in the authentication procedure.
73 * `views/auth/<METHOD>.haml`:
75 A template with the necessary form fields that will be used for
78 The authentication mechanism is set in the configuration file and
79 can include options specific to the method chosen.
81 Implementation details
82 ----------------------
84 Common application code lies in `Coquelicot::Application`, except for
85 one specific (and important) type of requests, namely `POST /update`.
86 These requests are handled directly at bare Rack level by
87 `Coquelicot::Rack::Upload`.
89 This allows to work directly with POST data as the browser is sending
90 it, so we can directly stream the uploaded file to our encrypted
93 The POST data must be in a very specific order, as we need to handle
94 authentication and other option fields before we start recording the
95 file content. Thanks to the W3C, the [HTML specification] states that
96 parts of the POST data must be delivered in the same order as the
97 controls appear in the `<form/>` container.
99 `Coquelicot::Rack::Multipart` exposes a simple DSL to parse the fields
100 as they are delivered. The later is used by `Coquelicot::Rack::Upload`
101 to perform its logic pretty nicely.
103 [HTML specification]: http://www.w3.org/TR/html4/interact/forms.html
105 Watch for buffered inputs!
106 --------------------------
108 Coquelicot is written in Ruby using Sinatra. Sinatra is based on the
109 Rack webserver interface. Rack specification mandates that applications
110 must be able to seek and rewind freely in the request content.
112 Request data is always received as a stream through the network. So in
113 order to comply with the specification, webservers implementing Rack
114 either buffer the input in memory (Webrick) or in a temporary file
115 (Thin, Passenger or Mongrel).
117 On top of that, when parsing `multipart/form-data` POST content,
118 `Rack::Request` (used by Sinatra) creates a new temporary file for
119 each files in the POST request.
121 For the specific needs of Coquelicot, these behaviours prevent users
122 from uploading large files (if `/tmp` is in memory) or breach their
123 privacy by writing a clear text version to disk.
125 To overcome these limitations, Coquelicot first uses a specific feature
126 of the Rainbows! webserver of streaming its input directly to
127 applications, and second bypasses `Rack::Request` to directly handle
128 POST content. Usage of any other Rack webserver is strongly discouraged
129 and should be restricted to development and testing.
134 Files are stored in the directory specified by the 'depot_path' setting.
135 One file in Coquelicot is actually stored in two files: one for metadata and
136 one for the file content.
140 The format is the following:
144 Salt: <8 bytes stored as Base64>
145 Expire-at: <expiration time in seconds since epoch>
149 Encryption is done using OpenSSL. Cipher is AES-256-CBC with key and IV
150 created using the `pbkdf2_hmac_sha1()` implementation of PKCS5. The later
151 is fed using the former *Salt* and the given passphrase, using 2000
154 Once decrypted, the metadata have the following format:
157 Created-at: <upload time in seconds since epoch>
158 Filename: "<original file name>"
159 Content-Type: "<MIME type>"
160 Length: <content length is bytes>
161 One-time-only: <true|false>
163 Headers must be valid YAML.
167 The content file contains the stored file in encrypted form. Encryption is done
168 with the same algorithm and keys as the encrypted metadata (see above).
170 The file name of the content file is the same as the one for metada, with an
171 added suffix of '.content'. For example, if the metadata file name is
172 `mqeb4pfcru2ymq3e6se7`, the associated content file will be
173 `mqeb4pfcru2ymq3e6se7.content`.
177 Both the content file and the metadata file are truncated to zero length when
182 In order to map download URLs to file name, a simple text file ".links"
183 is used. It contains a line for each file in the form:
185 <URL name> <metadata file name>
190 : Current version described above.
193 : File content is in the same file as the metadata. Content is put in the
194 after the metadata and an extra "--- \n".
199 Please send patches to the users and developers [mailing list]. They are best
200 prepared using `git format-patch`.
202 [mailing list]: https://listes.potager.org/listinfo/coquelicot
204 How to make a new release?
205 --------------------------
207 1. Bump version number in `lib/coquelicot/version.rb` and `Gemfile.lock`.
208 Don't forget to commit the changes.
210 2. Add a new entry in the NEWS file. For an outline:
212 git log --reverse --oneline $(git describe --abbrev=0)..
214 Don't forget to commit the changes.
218 git tag -s coquelicot-$VERSION -m "coquelicot $VERSION"
220 4. Push changes to the main repository:
222 git push origin master coquelicot-$VERSION
224 5. Create a source tarball:
226 bundle exec rake create_archive
230 gpg --armor --detach-sign coquelicot-$VERSION.tar.gz
232 7. Switch to the website:
236 8. Move the source tarball and signature to the website:
238 mv ../git/coquelicot-$VERSION.tar.gz* static/dist/
240 9. Add them to the website repository:
242 git add static/dist/coquelicot-$VERSION.tar.gz*
244 10. Update the version on the website homepage:
246 sed -e "s/coquelicot-$PREVIOUS_VERSION/coquelicot-$VERSION/g" \
249 11. Commit changes to the website.
251 12. Push the updated website:
254 git push origin master
256 13. Announce the release on `coquelicot@potager.org` mailing-list.
258 14. Announce the release on `freecode.com`.