├── .gitignore ├── .travis.yml ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── assets ├── css │ ├── main.css │ ├── normalize.css │ └── normalize.min.css ├── index.html ├── js │ └── vendor │ │ └── html5shiv.js └── script └── src ├── controllers.rs ├── database.rs ├── note.rs ├── paste.rs ├── settings.rs └── util.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.so 4 | *.rlib 5 | *.dll 6 | 7 | # Executables 8 | *.exe 9 | 10 | # Generated by Cargo 11 | /target/ 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | os: 3 | - linux 4 | - osx 5 | env: 6 | global: 7 | - secure: j6zAJhNExLUw+J8hr1M9//m52ovHtHWCFgOjULKYFUDywvdSh2uivZUI6INDfq4eWxLtX8lZP/HhqUjPERtkCZ+3y8xXMxAxxRmiWlOS6AO08wwZAxzBnX+wlZ6vwkdkv+OzWh8Zst+F7Ebz0geEEoiGUWEi3sr+ErQ+99BR4u4= 8 | - LD_LIBRARY_PATH: /usr/local/lib 9 | before_script: 10 | - rustc -v 11 | - cargo -V 12 | script: 13 | - cargo build -v 14 | - cargo test -v 15 | - cargo doc -v 16 | after_success: 17 | # Temporary fix for Rust-CI 18 | - cp -R target/doc doc 19 | - curl http://www.rust-ci.org/artifacts/put?t=$RUSTCI_TOKEN | sh 20 | - rm -r doc 21 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [root] 2 | name = "paste" 3 | version = "0.0.0" 4 | dependencies = [ 5 | "mysql 0.0.0 (git+https://github.com/Indiv0/rust-mysql-simple#a390eb7a70584f7801fa994dbaaa2fe15fc35301)", 6 | "nickel 0.1.0 (git+https://github.com/nickel-org/nickel.rs#8c8b656700ffb41a498d0e6806817c32066abab1)", 7 | "url 0.1.0 (git+https://github.com/servo/rust-url#bfdf809365600a7941a77524f9bb065886de3379)", 8 | ] 9 | 10 | [[package]] 11 | name = "anymap" 12 | version = "0.9.0" 13 | source = "git+https://github.com/chris-morgan/anymap.git#ad7ea81ba60233358fe3a025f08ee05537c3eb4d" 14 | 15 | [[package]] 16 | name = "encoding" 17 | version = "0.1.0" 18 | source = "git+https://github.com/lifthrasiir/rust-encoding#35f0d70f65f73ba16f296f9ec675eddee661ba79" 19 | 20 | [[package]] 21 | name = "http" 22 | version = "0.1.0-pre" 23 | source = "git+https://github.com/chris-morgan/rust-http.git#c1d601c1150ec6e10067dc0e022590277985b785" 24 | dependencies = [ 25 | "openssl 0.0.0 (git+https://github.com/sfackler/rust-openssl.git#a495465b75ffb18ff2303c5a11a103e00a15a13d)", 26 | "url 0.1.0 (git+https://github.com/servo/rust-url#bfdf809365600a7941a77524f9bb065886de3379)", 27 | ] 28 | 29 | [[package]] 30 | name = "lazy_static" 31 | version = "0.1.0" 32 | source = "git+https://github.com/Kimundi/lazy-static.rs#e62a65372f1dd9019e37eb9381d819edff80e360" 33 | 34 | [[package]] 35 | name = "mysql" 36 | version = "0.0.0" 37 | source = "git+https://github.com/Indiv0/rust-mysql-simple#a390eb7a70584f7801fa994dbaaa2fe15fc35301" 38 | dependencies = [ 39 | "lazy_static 0.1.0 (git+https://github.com/Kimundi/lazy-static.rs#e62a65372f1dd9019e37eb9381d819edff80e360)", 40 | ] 41 | 42 | [[package]] 43 | name = "nickel" 44 | version = "0.1.0" 45 | source = "git+https://github.com/nickel-org/nickel.rs#8c8b656700ffb41a498d0e6806817c32066abab1" 46 | dependencies = [ 47 | "anymap 0.9.0 (git+https://github.com/chris-morgan/anymap.git#ad7ea81ba60233358fe3a025f08ee05537c3eb4d)", 48 | "http 0.1.0-pre (git+https://github.com/chris-morgan/rust-http.git#c1d601c1150ec6e10067dc0e022590277985b785)", 49 | "rust-mustache 0.3.0 (git+https://github.com/erickt/rust-mustache.git#687d8fa41f998ec73c891de3f8ef41b4d3dc7b32)", 50 | "url 0.1.0 (git+https://github.com/servo/rust-url#bfdf809365600a7941a77524f9bb065886de3379)", 51 | ] 52 | 53 | [[package]] 54 | name = "openssl" 55 | version = "0.0.0" 56 | source = "git+https://github.com/sfackler/rust-openssl.git#a495465b75ffb18ff2303c5a11a103e00a15a13d" 57 | 58 | [[package]] 59 | name = "rust-mustache" 60 | version = "0.3.0" 61 | source = "git+https://github.com/erickt/rust-mustache.git#687d8fa41f998ec73c891de3f8ef41b4d3dc7b32" 62 | 63 | [[package]] 64 | name = "url" 65 | version = "0.1.0" 66 | source = "git+https://github.com/servo/rust-url#bfdf809365600a7941a77524f9bb065886de3379" 67 | dependencies = [ 68 | "encoding 0.1.0 (git+https://github.com/lifthrasiir/rust-encoding#35f0d70f65f73ba16f296f9ec675eddee661ba79)", 69 | ] 70 | 71 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "paste" 4 | version = "0.0.0" 5 | authors = [ "Nikita Pekin " ] 6 | 7 | [[bin]] 8 | 9 | name = "paste" 10 | 11 | [dependencies.nickel] 12 | 13 | git = "https://github.com/nickel-org/nickel.rs" 14 | 15 | [dependencies.url] 16 | 17 | git = "https://github.com/servo/rust-url" 18 | 19 | [dependencies.mysql] 20 | 21 | git = "https://github.com/Indiv0/rust-mysql-simple" 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Nikita Pekin 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the {organization} nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # paste-rs [![Build Status](https://travis-ci.org/Indiv0/paste-rs.svg?branch=master)](https://travis-ci.org/Indiv0/paste-rs) 2 | 3 | ## Database 4 | 5 | Setup the database with something like the following: 6 | 7 | ```SQL 8 | CREATE USER 'pasteuser'@'localhost' IDENTIFIED BY 'pastepass'; 9 | 10 | CREATE DATABASE IF NOT EXISTS paste; 11 | USE paste; 12 | CREATE TABLE IF NOT EXISTS paste ( 13 | id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 14 | code TEXT, 15 | time_created TIMESTAMP, 16 | data BLOB 17 | ); 18 | 19 | GRANT SELECT, INSERT, UPDATE, DELETE ON paste.paste TO 'pasteuser'@'localhost'; 20 | FLUSH PRIVILEGES; 21 | ``` 22 | 23 | ## Credits 24 | 25 | This project was inspired by [pxqz](https://github.com/Uiri/pxqz) by my friend [Uiri](https://github.com/Uiri). 26 | 27 | Additional inspiration (particularly for the nickel.rs routing and layout) from [superlogical/rusty](https://github.com/superlogical/rusty). 28 | 29 | Dependencies used: 30 | 31 | * [nickel.rs](https://github.com/nickel-org/nickel.rs) 32 | * [rust-url](https://github.com/servo/rust-url) 33 | * my fork of [rust-mysql-simple](https://github.com/blackbeam/rust-mysql-simple) 34 | -------------------------------------------------------------------------------- /assets/css/main.css: -------------------------------------------------------------------------------- 1 | /*! HTML5 Boilerplate v4.3.0 | MIT License | http://h5bp.com/ */ 2 | 3 | html, 4 | button, 5 | input, 6 | select, 7 | textarea { 8 | color: #222; 9 | } 10 | 11 | html { 12 | font-size: 1em; 13 | line-height: 1.4; 14 | } 15 | 16 | ::-moz-selection { 17 | background: #b3d4fc; 18 | text-shadow: none; 19 | } 20 | 21 | ::selection { 22 | background: #b3d4fc; 23 | text-shadow: none; 24 | } 25 | 26 | hr { 27 | display: block; 28 | height: 1px; 29 | border: 0; 30 | border-top: 1px solid #ccc; 31 | margin: 1em 0; 32 | padding: 0; 33 | } 34 | 35 | audio, 36 | canvas, 37 | img, 38 | video { 39 | vertical-align: middle; 40 | } 41 | 42 | fieldset { 43 | border: 0; 44 | margin: 0; 45 | padding: 0; 46 | } 47 | 48 | textarea { 49 | resize: vertical; 50 | } 51 | 52 | .browsehappy { 53 | margin: 0.2em 0; 54 | background: #ccc; 55 | color: #000; 56 | padding: 0.2em 0; 57 | } 58 | 59 | 60 | /* ========================================================================== 61 | Author's custom styles 62 | ========================================================================== */ 63 | 64 | header { 65 | text-align: center; 66 | } 67 | 68 | .container { 69 | width: 90%; 70 | } 71 | 72 | .wrapper { 73 | width: 90%; 74 | margin: 0px 5%; 75 | } 76 | 77 | textarea { 78 | width: 100%; 79 | border-top: 2px solid #ccc; 80 | border-left: 2px solid #ccc; 81 | border-right: 2px solid #eee; 82 | border-bottom: 2px solid #eee; 83 | } 84 | 85 | textarea:focus { 86 | outline: 0; 87 | border-color: #4697e4; 88 | } 89 | 90 | .input { 91 | padding: 1px; 92 | margin: 0 0 10px 0; 93 | } 94 | 95 | 96 | /* ========================================================================== 97 | Media Queries 98 | ========================================================================== */ 99 | 100 | @media only screen and (min-width: 35em) { 101 | 102 | } 103 | 104 | @media print, 105 | (-o-min-device-pixel-ratio: 5/4), 106 | (-webkit-min-device-pixel-ratio: 1.25), 107 | (min-resolution: 120dpi) { 108 | 109 | } 110 | 111 | /* ========================================================================== 112 | Helper classes 113 | ========================================================================== */ 114 | 115 | .ir { 116 | background-color: transparent; 117 | border: 0; 118 | overflow: hidden; 119 | *text-indent: -9999px; 120 | } 121 | 122 | .ir:before { 123 | content: ""; 124 | display: block; 125 | width: 0; 126 | height: 150%; 127 | } 128 | 129 | .hidden { 130 | display: none !important; 131 | visibility: hidden; 132 | } 133 | 134 | .visuallyhidden { 135 | border: 0; 136 | clip: rect(0 0 0 0); 137 | height: 1px; 138 | margin: -1px; 139 | overflow: hidden; 140 | padding: 0; 141 | position: absolute; 142 | width: 1px; 143 | } 144 | 145 | .visuallyhidden.focusable:active, 146 | .visuallyhidden.focusable:focus { 147 | clip: auto; 148 | height: auto; 149 | margin: 0; 150 | overflow: visible; 151 | position: static; 152 | width: auto; 153 | } 154 | 155 | .invisible { 156 | visibility: hidden; 157 | } 158 | 159 | .clearfix:before, 160 | .clearfix:after { 161 | content: " "; 162 | display: table; 163 | } 164 | 165 | .clearfix:after { 166 | clear: both; 167 | } 168 | 169 | .clearfix { 170 | *zoom: 1; 171 | } 172 | 173 | /* ========================================================================== 174 | Print styles 175 | ========================================================================== */ 176 | 177 | @media print { 178 | * { 179 | background: transparent !important; 180 | color: #000 !important; 181 | box-shadow: none !important; 182 | text-shadow: none !important; 183 | } 184 | 185 | a, 186 | a:visited { 187 | text-decoration: underline; 188 | } 189 | 190 | a[href]:after { 191 | content: " (" attr(href) ")"; 192 | } 193 | 194 | abbr[title]:after { 195 | content: " (" attr(title) ")"; 196 | } 197 | 198 | .ir a:after, 199 | a[href^="javascript:"]:after, 200 | a[href^="#"]:after { 201 | content: ""; 202 | } 203 | 204 | pre, 205 | blockquote { 206 | border: 1px solid #999; 207 | page-break-inside: avoid; 208 | } 209 | 210 | thead { 211 | display: table-header-group; 212 | } 213 | 214 | tr, 215 | img { 216 | page-break-inside: avoid; 217 | } 218 | 219 | img { 220 | max-width: 100% !important; 221 | } 222 | 223 | @page { 224 | margin: 0.5cm; 225 | } 226 | 227 | p, 228 | h2, 229 | h3 { 230 | orphans: 3; 231 | widows: 3; 232 | } 233 | 234 | h2, 235 | h3 { 236 | page-break-after: avoid; 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /assets/css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v1.1.3 | MIT License | git.io/normalize */ 2 | 3 | /* ========================================================================== 4 | HTML5 display definitions 5 | ========================================================================== */ 6 | 7 | /** 8 | * Correct `block` display not defined in IE 6/7/8/9 and Firefox 3. 9 | */ 10 | 11 | article, 12 | aside, 13 | details, 14 | figcaption, 15 | figure, 16 | footer, 17 | header, 18 | hgroup, 19 | main, 20 | nav, 21 | section, 22 | summary { 23 | display: block; 24 | } 25 | 26 | /** 27 | * Correct `inline-block` display not defined in IE 6/7/8/9 and Firefox 3. 28 | */ 29 | 30 | audio, 31 | canvas, 32 | video { 33 | display: inline-block; 34 | *display: inline; 35 | *zoom: 1; 36 | } 37 | 38 | /** 39 | * Prevent modern browsers from displaying `audio` without controls. 40 | * Remove excess height in iOS 5 devices. 41 | */ 42 | 43 | audio:not([controls]) { 44 | display: none; 45 | height: 0; 46 | } 47 | 48 | /** 49 | * Address styling not present in IE 7/8/9, Firefox 3, and Safari 4. 50 | * Known issue: no IE 6 support. 51 | */ 52 | 53 | [hidden] { 54 | display: none; 55 | } 56 | 57 | /* ========================================================================== 58 | Base 59 | ========================================================================== */ 60 | 61 | /** 62 | * 1. Correct text resizing oddly in IE 6/7 when body `font-size` is set using 63 | * `em` units. 64 | * 2. Prevent iOS text size adjust after orientation change, without disabling 65 | * user zoom. 66 | */ 67 | 68 | html { 69 | font-size: 100%; /* 1 */ 70 | -ms-text-size-adjust: 100%; /* 2 */ 71 | -webkit-text-size-adjust: 100%; /* 2 */ 72 | } 73 | 74 | /** 75 | * Address `font-family` inconsistency between `textarea` and other form 76 | * elements. 77 | */ 78 | 79 | html, 80 | button, 81 | input, 82 | select, 83 | textarea { 84 | font-family: sans-serif; 85 | } 86 | 87 | /** 88 | * Address margins handled incorrectly in IE 6/7. 89 | */ 90 | 91 | body { 92 | margin: 0; 93 | } 94 | 95 | /* ========================================================================== 96 | Links 97 | ========================================================================== */ 98 | 99 | /** 100 | * Address `outline` inconsistency between Chrome and other browsers. 101 | */ 102 | 103 | a:focus { 104 | outline: thin dotted; 105 | } 106 | 107 | /** 108 | * Improve readability when focused and also mouse hovered in all browsers. 109 | */ 110 | 111 | a:active, 112 | a:hover { 113 | outline: 0; 114 | } 115 | 116 | /* ========================================================================== 117 | Typography 118 | ========================================================================== */ 119 | 120 | /** 121 | * Address font sizes and margins set differently in IE 6/7. 122 | * Address font sizes within `section` and `article` in Firefox 4+, Safari 5, 123 | * and Chrome. 124 | */ 125 | 126 | h1 { 127 | font-size: 2em; 128 | margin: 0.67em 0; 129 | } 130 | 131 | h2 { 132 | font-size: 1.5em; 133 | margin: 0.83em 0; 134 | } 135 | 136 | h3 { 137 | font-size: 1.17em; 138 | margin: 1em 0; 139 | } 140 | 141 | h4 { 142 | font-size: 1em; 143 | margin: 1.33em 0; 144 | } 145 | 146 | h5 { 147 | font-size: 0.83em; 148 | margin: 1.67em 0; 149 | } 150 | 151 | h6 { 152 | font-size: 0.67em; 153 | margin: 2.33em 0; 154 | } 155 | 156 | /** 157 | * Address styling not present in IE 7/8/9, Safari 5, and Chrome. 158 | */ 159 | 160 | abbr[title] { 161 | border-bottom: 1px dotted; 162 | } 163 | 164 | /** 165 | * Address style set to `bolder` in Firefox 3+, Safari 4/5, and Chrome. 166 | */ 167 | 168 | b, 169 | strong { 170 | font-weight: bold; 171 | } 172 | 173 | blockquote { 174 | margin: 1em 40px; 175 | } 176 | 177 | /** 178 | * Address styling not present in Safari 5 and Chrome. 179 | */ 180 | 181 | dfn { 182 | font-style: italic; 183 | } 184 | 185 | /** 186 | * Address differences between Firefox and other browsers. 187 | * Known issue: no IE 6/7 normalization. 188 | */ 189 | 190 | hr { 191 | -moz-box-sizing: content-box; 192 | box-sizing: content-box; 193 | height: 0; 194 | } 195 | 196 | /** 197 | * Address styling not present in IE 6/7/8/9. 198 | */ 199 | 200 | mark { 201 | background: #ff0; 202 | color: #000; 203 | } 204 | 205 | /** 206 | * Address margins set differently in IE 6/7. 207 | */ 208 | 209 | p, 210 | pre { 211 | margin: 1em 0; 212 | } 213 | 214 | /** 215 | * Correct font family set oddly in IE 6, Safari 4/5, and Chrome. 216 | */ 217 | 218 | code, 219 | kbd, 220 | pre, 221 | samp { 222 | font-family: monospace, serif; 223 | _font-family: 'courier new', monospace; 224 | font-size: 1em; 225 | } 226 | 227 | /** 228 | * Improve readability of pre-formatted text in all browsers. 229 | */ 230 | 231 | pre { 232 | white-space: pre; 233 | white-space: pre-wrap; 234 | word-wrap: break-word; 235 | } 236 | 237 | /** 238 | * Address CSS quotes not supported in IE 6/7. 239 | */ 240 | 241 | q { 242 | quotes: none; 243 | } 244 | 245 | /** 246 | * Address `quotes` property not supported in Safari 4. 247 | */ 248 | 249 | q:before, 250 | q:after { 251 | content: ''; 252 | content: none; 253 | } 254 | 255 | /** 256 | * Address inconsistent and variable font size in all browsers. 257 | */ 258 | 259 | small { 260 | font-size: 80%; 261 | } 262 | 263 | /** 264 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 265 | */ 266 | 267 | sub, 268 | sup { 269 | font-size: 75%; 270 | line-height: 0; 271 | position: relative; 272 | vertical-align: baseline; 273 | } 274 | 275 | sup { 276 | top: -0.5em; 277 | } 278 | 279 | sub { 280 | bottom: -0.25em; 281 | } 282 | 283 | /* ========================================================================== 284 | Lists 285 | ========================================================================== */ 286 | 287 | /** 288 | * Address margins set differently in IE 6/7. 289 | */ 290 | 291 | dl, 292 | menu, 293 | ol, 294 | ul { 295 | margin: 1em 0; 296 | } 297 | 298 | dd { 299 | margin: 0 0 0 40px; 300 | } 301 | 302 | /** 303 | * Address paddings set differently in IE 6/7. 304 | */ 305 | 306 | menu, 307 | ol, 308 | ul { 309 | padding: 0 0 0 40px; 310 | } 311 | 312 | /** 313 | * Correct list images handled incorrectly in IE 7. 314 | */ 315 | 316 | nav ul, 317 | nav ol { 318 | list-style: none; 319 | list-style-image: none; 320 | } 321 | 322 | /* ========================================================================== 323 | Embedded content 324 | ========================================================================== */ 325 | 326 | /** 327 | * 1. Remove border when inside `a` element in IE 6/7/8/9 and Firefox 3. 328 | * 2. Improve image quality when scaled in IE 7. 329 | */ 330 | 331 | img { 332 | border: 0; /* 1 */ 333 | -ms-interpolation-mode: bicubic; /* 2 */ 334 | } 335 | 336 | /** 337 | * Correct overflow displayed oddly in IE 9. 338 | */ 339 | 340 | svg:not(:root) { 341 | overflow: hidden; 342 | } 343 | 344 | /* ========================================================================== 345 | Figures 346 | ========================================================================== */ 347 | 348 | /** 349 | * Address margin not present in IE 6/7/8/9, Safari 5, and Opera 11. 350 | */ 351 | 352 | figure { 353 | margin: 0; 354 | } 355 | 356 | /* ========================================================================== 357 | Forms 358 | ========================================================================== */ 359 | 360 | /** 361 | * Correct margin displayed oddly in IE 6/7. 362 | */ 363 | 364 | form { 365 | margin: 0; 366 | } 367 | 368 | /** 369 | * Define consistent border, margin, and padding. 370 | */ 371 | 372 | fieldset { 373 | border: 1px solid #c0c0c0; 374 | margin: 0 2px; 375 | padding: 0.35em 0.625em 0.75em; 376 | } 377 | 378 | /** 379 | * 1. Correct color not being inherited in IE 6/7/8/9. 380 | * 2. Correct text not wrapping in Firefox 3. 381 | * 3. Correct alignment displayed oddly in IE 6/7. 382 | */ 383 | 384 | legend { 385 | border: 0; /* 1 */ 386 | padding: 0; 387 | white-space: normal; /* 2 */ 388 | *margin-left: -7px; /* 3 */ 389 | } 390 | 391 | /** 392 | * 1. Correct font size not being inherited in all browsers. 393 | * 2. Address margins set differently in IE 6/7, Firefox 3+, Safari 5, 394 | * and Chrome. 395 | * 3. Improve appearance and consistency in all browsers. 396 | */ 397 | 398 | button, 399 | input, 400 | select, 401 | textarea { 402 | font-size: 100%; /* 1 */ 403 | margin: 0; /* 2 */ 404 | vertical-align: baseline; /* 3 */ 405 | *vertical-align: middle; /* 3 */ 406 | } 407 | 408 | /** 409 | * Address Firefox 3+ setting `line-height` on `input` using `!important` in 410 | * the UA stylesheet. 411 | */ 412 | 413 | button, 414 | input { 415 | line-height: normal; 416 | } 417 | 418 | /** 419 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 420 | * All other form control elements do not inherit `text-transform` values. 421 | * Correct `button` style inheritance in Chrome, Safari 5+, and IE 6+. 422 | * Correct `select` style inheritance in Firefox 4+ and Opera. 423 | */ 424 | 425 | button, 426 | select { 427 | text-transform: none; 428 | } 429 | 430 | /** 431 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 432 | * and `video` controls. 433 | * 2. Correct inability to style clickable `input` types in iOS. 434 | * 3. Improve usability and consistency of cursor style between image-type 435 | * `input` and others. 436 | * 4. Remove inner spacing in IE 7 without affecting normal text inputs. 437 | * Known issue: inner spacing remains in IE 6. 438 | */ 439 | 440 | button, 441 | html input[type="button"], /* 1 */ 442 | input[type="reset"], 443 | input[type="submit"] { 444 | -webkit-appearance: button; /* 2 */ 445 | cursor: pointer; /* 3 */ 446 | *overflow: visible; /* 4 */ 447 | } 448 | 449 | /** 450 | * Re-set default cursor for disabled elements. 451 | */ 452 | 453 | button[disabled], 454 | html input[disabled] { 455 | cursor: default; 456 | } 457 | 458 | /** 459 | * 1. Address box sizing set to content-box in IE 8/9. 460 | * 2. Remove excess padding in IE 8/9. 461 | * 3. Remove excess padding in IE 7. 462 | * Known issue: excess padding remains in IE 6. 463 | */ 464 | 465 | input[type="checkbox"], 466 | input[type="radio"] { 467 | box-sizing: border-box; /* 1 */ 468 | padding: 0; /* 2 */ 469 | *height: 13px; /* 3 */ 470 | *width: 13px; /* 3 */ 471 | } 472 | 473 | /** 474 | * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 475 | * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome 476 | * (include `-moz` to future-proof). 477 | */ 478 | 479 | input[type="search"] { 480 | -webkit-appearance: textfield; /* 1 */ 481 | -moz-box-sizing: content-box; 482 | -webkit-box-sizing: content-box; /* 2 */ 483 | box-sizing: content-box; 484 | } 485 | 486 | /** 487 | * Remove inner padding and search cancel button in Safari 5 and Chrome 488 | * on OS X. 489 | */ 490 | 491 | input[type="search"]::-webkit-search-cancel-button, 492 | input[type="search"]::-webkit-search-decoration { 493 | -webkit-appearance: none; 494 | } 495 | 496 | /** 497 | * Remove inner padding and border in Firefox 3+. 498 | */ 499 | 500 | button::-moz-focus-inner, 501 | input::-moz-focus-inner { 502 | border: 0; 503 | padding: 0; 504 | } 505 | 506 | /** 507 | * 1. Remove default vertical scrollbar in IE 6/7/8/9. 508 | * 2. Improve readability and alignment in all browsers. 509 | */ 510 | 511 | textarea { 512 | overflow: auto; /* 1 */ 513 | vertical-align: top; /* 2 */ 514 | } 515 | 516 | /* ========================================================================== 517 | Tables 518 | ========================================================================== */ 519 | 520 | /** 521 | * Remove most spacing between table cells. 522 | */ 523 | 524 | table { 525 | border-collapse: collapse; 526 | border-spacing: 0; 527 | } 528 | -------------------------------------------------------------------------------- /assets/css/normalize.min.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v1.1.3 | MIT License | git.io/normalize */article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none;height:0}[hidden]{display:none}html{font-size:100%;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}html,button,input,select,textarea{font-family:sans-serif}body{margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{font-size:2em;margin:.67em 0}h2{font-size:1.5em;margin:.83em 0}h3{font-size:1.17em;margin:1em 0}h4{font-size:1em;margin:1.33em 0}h5{font-size:.83em;margin:1.67em 0}h6{font-size:.67em;margin:2.33em 0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}blockquote{margin:1em 40px}dfn{font-style:italic}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}mark{background:#ff0;color:#000}p,pre{margin:1em 0}code,kbd,pre,samp{font-family:monospace,serif;_font-family:'courier new',monospace;font-size:1em}pre{white-space:pre;white-space:pre-wrap;word-wrap:break-word}q{quotes:none}q:before,q:after{content:'';content:none}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}dl,menu,ol,ul{margin:1em 0}dd{margin:0 0 0 40px}menu,ol,ul{padding:0 0 0 40px}nav ul,nav ol{list-style:none;list-style-image:none}img{border:0;-ms-interpolation-mode:bicubic}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0;white-space:normal;*margin-left:-7px}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;*overflow:visible}button[disabled],html input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*height:13px;*width:13px}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0} -------------------------------------------------------------------------------- /assets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | paste-rs 10 | 11 | 12 | 13 | 14 | 15 | 19 | 20 | 21 |
22 |
23 |

paste-rs

24 |

A paste tool written entirely in rust

25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | 36 |

I've also written a command-line tool in Python3. 37 | Input your paste via stdin and a URL to the paste (in text/plain) is returned on stdout. 38 | The script is available here or you can install it by running:

39 |
40 | curl p.nikitapek.in/script > pasters; chmod 755 pasters; su -c "mv pasters /usr/local/bin"
41 |             
42 |

to download it and put it in your path (do you trust me :) ?).

43 |
44 |
45 | 46 |
47 |
48 |

This entire system is written in rust. 49 | For more information (and the source), check out this project's GitHub page here.

50 |
51 |
52 | 53 | 54 | -------------------------------------------------------------------------------- /assets/js/vendor/html5shiv.js: -------------------------------------------------------------------------------- 1 | (function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag(); 2 | a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/\w+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x"; 3 | c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode|| 4 | "undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f);if(g)return a.createDocumentFragment(); 5 | for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d match x.split('=').skip(1).next() { 38 | Some(value) => percent_encoding::lossy_utf8_percent_decode(value.as_bytes()), 39 | None => { 40 | response.send(r#"{ "error": "failed to process form" }"#); 41 | return 42 | } 43 | }, 44 | None => { 45 | response.send(r#"{ "error": "failed to process form" }"#); 46 | return 47 | } 48 | }; 49 | 50 | let mut code: Option = None; 51 | for i in range(0u, 50u) { 52 | // If the code generator collides 50 times in a row, quit. 53 | if i == 49 { 54 | response.origin.status = InternalServerError; 55 | response.send("Dem collisions doe"); 56 | return 57 | } 58 | 59 | let generated_code = util::random_string(settings::RANDOM_CODE_LENGTH); 60 | 61 | // Verify the code is not already taken. 62 | match Note::find_by_code(generated_code.as_slice()) { 63 | Some(_) => {}, 64 | None => { 65 | code = Some(generated_code); 66 | break 67 | } 68 | } 69 | } 70 | let code = code.unwrap(); 71 | let mut note = Note::new(0, code.clone().as_slice(), time::now_utc().to_timespec(), data.as_slice()); 72 | 73 | Note::insert(&mut note); 74 | 75 | response.origin.status = Found; 76 | response.origin.headers.location = Some(Url::parse(format!("{}/{}", settings::BASE_URL, code).as_slice()).unwrap()); 77 | } 78 | 79 | pub fn get_note(request: &Request, response: &mut Response) { 80 | response.origin.headers.content_type = Some(MediaType { 81 | type_: "text".to_string(), 82 | subtype: "plain".to_string(), 83 | parameters: vec!((String::from_str("charset"), String::from_str("UTF-8"))) 84 | }); 85 | 86 | let code = request.params.index(&"code".to_string()); 87 | 88 | let note = Note::find_by_code(code.as_slice()); 89 | 90 | match note { 91 | Some(note) => { 92 | response.send(note.data.clone()); 93 | }, 94 | None => { 95 | response.set_content_type("html"); 96 | response.origin.status = NotFound; 97 | response.send("

404 - Paste Not Found

"); 98 | } 99 | }; 100 | } 101 | 102 | pub fn custom_404(err: &NickelError, _req: &Request, response: &mut Response) -> Result { 103 | match err.kind { 104 | ErrorWithStatusCode(NotFound) => { 105 | response.set_content_type("html"); 106 | response.origin.status = NotFound; 107 | response.send("

404 - Paste Not Found

"); 108 | Ok(Halt) 109 | }, 110 | _ => Ok(Continue) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/database.rs: -------------------------------------------------------------------------------- 1 | use mysql::conn::MyOpts; 2 | use mysql::conn::pool::MyPool; 3 | use mysql::error::MyError; 4 | use std::default::Default; 5 | 6 | use settings; 7 | 8 | pub struct Database { 9 | opts: MyOpts 10 | } 11 | 12 | impl Database { 13 | pub fn new() -> Database { 14 | let opts = MyOpts { 15 | user: Some(settings::DATABASE_USERNAME.to_string()), 16 | pass: Some(settings::DATABASE_PASSWORD.to_string()), 17 | db_name: Some(settings::DATABASE_NAME.to_string()), 18 | ..Default::default() 19 | }; 20 | 21 | Database { 22 | opts: opts 23 | } 24 | } 25 | 26 | pub fn connect(&self) -> MyPool { 27 | MyPool::new(self.opts.clone()).unwrap() 28 | } 29 | 30 | /// Sets up the database and table required. 31 | /// For this to work, priveleged user and password must be specified in settings. 32 | /// Additionally, no database name must be specified. 33 | pub fn create(&self) -> Result<(), MyError> { 34 | let conn = self.connect(); 35 | 36 | try!(conn.query("CREATE DATABASE IF NOT EXISTS paste")); 37 | try!(conn.query("USE paste")); 38 | try!(conn.query("CREATE TABLE IF NOT EXISTS paste ( 39 | id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 40 | code TEXT, 41 | time_created TIMESTAMP, 42 | data BLOB)")); 43 | Ok(()) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/note.rs: -------------------------------------------------------------------------------- 1 | use mysql::value::{ 2 | from_value, 3 | Value, 4 | }; 5 | use time::Timespec; 6 | 7 | use database::Database; 8 | 9 | #[deriving(Clone, PartialEq, Show)] 10 | pub struct Note { 11 | pub id: i32, 12 | pub code: String, 13 | pub time_created: Timespec, 14 | pub data: String 15 | } 16 | 17 | impl Note { 18 | pub fn new(id: i32, code: &str, time_created: Timespec, data: &str) -> Note { 19 | Note { 20 | id: id, 21 | code: code.to_string(), 22 | time_created: time_created, 23 | data: data.to_string() 24 | } 25 | } 26 | 27 | pub fn from_row(row: &Vec) -> Note { 28 | Note { 29 | id: from_value(&row[0]), 30 | code: from_value(&row[1]), 31 | time_created: from_value(&row[2]), 32 | data: from_value(&row[3]) 33 | } 34 | } 35 | 36 | pub fn find_by_code(code: &str) -> Option { 37 | let conn = Database::new().connect(); 38 | let mut stmt = match conn.prepare("SELECT * FROM paste \ 39 | WHERE code = ? LIMIT 1") { 40 | Ok(stmt) => stmt, 41 | Err(e) => { 42 | println!("Error, failed to find Note: {}", e); 43 | return None 44 | } 45 | }; 46 | 47 | for row in stmt.execute(&[&code.to_string()]).unwrap() { 48 | let row = row.unwrap(); 49 | return Some(Note::from_row(&row)) 50 | } 51 | 52 | None 53 | } 54 | 55 | pub fn insert(note: &mut Note) { 56 | let conn = Database::new().connect(); 57 | let mut stmt = conn.prepare(" 58 | INSERT INTO paste ( 59 | code, 60 | time_created, 61 | data 62 | ) 63 | VALUES (?, ?, ?);").unwrap(); 64 | 65 | for row in stmt.execute(&[¬e.code, ¬e.time_created, ¬e.data]).unwrap() { 66 | let row = row.unwrap(); 67 | let id = from_value(&row[0]); 68 | note.id = id; 69 | break; 70 | } 71 | } 72 | 73 | pub fn _all() -> Vec { 74 | let mut notes: Vec = Vec::new(); 75 | let conn = Database::new().connect(); 76 | let mut stmt = conn.prepare("SELECT id, code, time_created, data FROM paste;").unwrap(); 77 | 78 | for row in stmt.execute([]).unwrap() { 79 | let row = row.unwrap(); 80 | let note = Note::from_row(&row); 81 | notes.push(note); 82 | } 83 | notes 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/paste.rs: -------------------------------------------------------------------------------- 1 | extern crate http; 2 | extern crate nickel; 3 | extern crate mysql; 4 | extern crate serialize; 5 | extern crate time; 6 | extern crate url; 7 | 8 | use std::io::net::ip::Ipv4Addr; 9 | use nickel::{ 10 | IntoErrorHandler, 11 | Nickel, 12 | }; 13 | 14 | use database::Database; 15 | 16 | mod controllers; 17 | mod database; 18 | mod note; 19 | mod settings; 20 | mod util; 21 | 22 | fn main() { 23 | // Initialize database connection. 24 | let database = Database::new(); 25 | if settings::CREATE_DATABASE { 26 | println!("Creating database"); 27 | match database.create() { 28 | Ok(_) => println!("Initialized database"), 29 | Err(e) => fail!("Failed to initialize database: {}", e) 30 | } 31 | } 32 | 33 | // Create web server. 34 | let mut server = Nickel::new(); 35 | let mut router = Nickel::router(); 36 | 37 | // Routes. 38 | router.get("/:code", controllers::get_note); 39 | router.post("/", controllers::post_note); 40 | 41 | // Middleware. 42 | server.utilize(Nickel::static_files("assets/")); 43 | server.utilize(router); 44 | 45 | // Error handling. 46 | server.handle_error(IntoErrorHandler::from_fn(controllers::custom_404)); 47 | 48 | // Start web server. 49 | server.listen(Ipv4Addr(127, 0, 0, 1), 3000); 50 | } 51 | -------------------------------------------------------------------------------- /src/settings.rs: -------------------------------------------------------------------------------- 1 | pub static DATABASE_USERNAME: &'static str = "pasteuser"; 2 | pub static DATABASE_PASSWORD: &'static str = "pastepass"; 3 | pub static DATABASE_NAME: &'static str = "paste"; 4 | 5 | // Whether or not to attempt to create and setup the database. 6 | pub static CREATE_DATABASE: bool = false; 7 | 8 | // The length of the random string designating a link to a paste. 9 | pub static RANDOM_CODE_LENGTH: uint = 6; 10 | 11 | // The base url for the website (used for relative links and redirects). 12 | pub static BASE_URL: &'static str = "http://127.0.0.1:3000"; 13 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | use std::rand::{ 2 | task_rng, 3 | Rng, 4 | }; 5 | 6 | pub fn random_string(length: uint) -> String { 7 | let mut string = String::new(); 8 | for n in task_rng().gen_ascii_chars().take(length) { 9 | string.push_char(n); 10 | } 11 | string 12 | } 13 | --------------------------------------------------------------------------------