diff --git a/AUTHORS.md b/AUTHORS.md index 37f9464..46ead1b 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -8,3 +8,5 @@ * Chris https://github.com/ch0wnag3 (favicon URL improvement) * Gabriel Wanzek https://github.com/GabrielWanzek (gabdark & gabdark3 themes) * Luc https://github.com/ltribolet (Bootstrap theme) +* Stephan Bergemann https://github.com/oberling (German translation, Bootstrap multilang support + making bootstrap theme HTML5 valid) +* linuxman https://github.com/linuxman (Spanish translation) diff --git a/CC0 b/CC0 new file mode 100644 index 0000000..bdfff7a --- /dev/null +++ b/CC0 @@ -0,0 +1,118 @@ + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT + PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT + CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES + THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO + WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION + OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES + RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR + WORKS PROVIDED HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically +confer exclusive Copyright and Related Rights (defined below) upon the +creator and subsequent owner(s) (each and all, an "owner") of an +original work of authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work +for the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without +fear of later claims of infringement build upon, modify, incorporate +in other works, reuse and redistribute as freely as possible in any +form whatsoever and for any purposes, including without limitation +commercial purposes. These owners may contribute to the Commons to +promote the ideal of a free culture and the further production of +creative, cultural and scientific works, or to gain reputation or +greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or +she is an owner of Copyright and Related Rights in the Work, +voluntarily elects to apply CC0 to the Work and publicly distribute +the Work under its terms, with knowledge of his or her Copyright and +Related Rights in the Work and the meaning and intended legal effect +of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may +be protected by copyright and related or neighboring rights +("Copyright and Related Rights"). Copyright and Related Rights +include, but are not limited to, the following: + + the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; moral rights retained by the + original author(s) and/or performer(s); publicity and privacy + rights pertaining to a person's image or likeness depicted in a + Work; rights protecting against unfair competition in regards to a + Work, subject to the limitations in paragraph 4(a), below; rights + protecting the extraction, dissemination, use and reuse of data in + a Work; database rights (such as those arising under Directive + 96/9/EC of the European Parliament and of the Council of 11 March + 1996 on the legal protection of databases, and under any national + implementation thereof, including any amended or successor version + of such directive); and other similar, equivalent or corresponding + rights throughout the world based on applicable law or treaty, and + any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in +contravention of, applicable law, Affirmer hereby overtly, fully, +permanently, irrevocably and unconditionally waives, abandons, and +surrenders all of Affirmer's Copyright and Related Rights and +associated claims and causes of action, whether now known or unknown +(including existing as well as future claims and causes of action), in +the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"Waiver"). Affirmer makes the Waiver for the benefit of each member of +the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal +or equitable action to disrupt the quiet enjoyment of the Work by the +public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any +reason be judged legally invalid or ineffective under applicable law, +then the Waiver shall be preserved to the maximum extent permitted +taking into account Affirmer's express Statement of Purpose. In +addition, to the extent the Waiver is so judged Affirmer hereby grants +to each affected person a royalty-free, non transferable, non +sublicensable, non exclusive, irrevocable and unconditional license to +exercise Affirmer's Copyright and Related Rights in the Work (i) in +all territories worldwide, (ii) for the maximum duration provided by +applicable law or treaty (including future time extensions), (iii) in +any current or future medium and for any number of copies, and (iv) +for any purpose whatsoever, including without limitation commercial, +advertising or promotional purposes (the "License"). The License shall +be deemed effective as of the date CC0 was applied by Affirmer to the +Work. Should any part of the License for any reason be judged legally +invalid or ineffective under applicable law, such partial invalidity +or ineffectiveness shall not invalidate the remainder of the License, +and in such case Affirmer hereby affirms that he or she will not (i) +exercise any of his or her remaining Copyright and Related Rights in +the Work or (ii) assert any associated claims and causes of action +with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + No trademark or patent rights held by Affirmer are waived, + abandoned, surrendered, licensed or otherwise affected by this + document. Affirmer offers the Work as-is and makes no + representations or warranties of any kind concerning the Work, + express, implied, statutory or otherwise, including without + limitation warranties of title, merchantability, fitness for a + particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, + whether or not discoverable, all to the greatest extent + permissible under applicable law. Affirmer disclaims + responsibility for clearing rights of other persons that may apply + to the Work or any use thereof, including without limitation any + person's Copyright and Related Rights in the Work. Further, + Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. Affirmer understands and acknowledges that Creative Commons + is not a party to this document and has no duty or obligation with + respect to this CC0 or use of the Work. diff --git a/README.md b/README.md index 49ab739..0fa0aec 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,34 @@ Stikked is an Open-Source PHP Pastebin, with the aim of keeping a simple and easy to use user interface. -Stikked allows you to easily share code with anyone you wish. Stikked was created for the following reasons: +Stikked allows you to easily share code with anyone you wish. Based on the [original Stikked](http://code.google.com/p/stikked/) with lots of bugfixes and improvements. -* IRC and Private Chats were spammed. -* Pastebins were ugly. -* Pastebins were confusing. -* Pastebins were messy and not thought through. -* Stikked rethought code collaboration, by making it easy to paste code. +Here are some features: -Based on the original Stikked (http://code.google.com/p/stikked/) with lots of bugfixes and improvements. - -Thanks to Rebecca Chapnik for this great review: http://maketecheasier.com/run-your-own-pastebin-with-stikked/2013/01/11 +* Easy setup +* Syntaxhighlighting for many languages, including live syntaxhighlighting with CodeMirror +* Paste replies +* Diff view between the original paste and the reply +* An API +* Trending pastes +* Anti-Spam features +* Themes support +* Multilanguage support +* And many more. View [this review](http://maketecheasier.com/run-your-own-pastebin-with-stikked/2013/01/11) Try it out ---------- + http://paste.scratchbook.ch/ Installation ------------ -1. Download stikked from https://github.com/claudehohl/Stikked/tags -2. Create a user and database for Stikked -3. Edit configuration settings in application/config/stikked.php - everything is described there -4. You're done! +1. Download stikked from https://github.com/claudehohl/Stikked/tags +2. Create a user and database for Stikked +3. Edit configuration settings in application/config/stikked.php - everything is described there +4. You're done! * The database structure will be created automatically if it doesn't exist. * No special file permissions are needed by default. Optional: If you want to have the JavaScript- and CSS-files minified, the static/asset/ folder has to be writable. @@ -32,22 +36,64 @@ Installation * */5 * * * * curl --silent http://yoursite.com/cron/[key] +Documentation +------------- + +In the folder doc/, you will find: + +* Webserver example configurations for Apache, Nginx, Lighttpd, Cherokee +* A troubleshooting guide +* How to create your own theme +* How to translate Stikked into your language +* How to contribute and improve Stikked + + Changelog --------- ### Version 0.8.5: * Themes! Configure a different theme in config/stikked.php - or create your own +* Multilanguage support. Configure a different language in config/stikked.php +* Diff view for paste replies! View differences between the original paste and its reply + * see it in action: http://paste.scratchbook.ch/view/de81a093/diff +* Possibility to set default expiration time +* Updated GeSHi to version 1.0.8.11 +* Updated CodeMirror to version 3.11 +* Lots of minor fixes and improvements +* Added guides for troubleshooting, development, translation and creating themes +* Added webserver example configurations +* Added reCaptcha integration for better antispam #### Upgrade instructions -The following line must be present config/stikked.php +The following lines must be present config/stikked.php ```php $config['theme'] = 'default'; ``` -You can choose between default, bootstrap, gabdark and gabdark3. +You can choose between default, bootstrap, gabdark, gabdark3 and a fancy geocities theme ;) + +Create you own theme. See doc/CREATING_THEMES.md + +```php +$config['language'] = 'english'; +``` + +You can choose between english, german and swissgerman ;) + +Help translating Stikked into your language! See doc/TRANSLATING_STIKKED.md + +##### reCaptcha + +```php +$config['recaptcha_publickey'] = ''; +$config['recaptcha_privatekey'] = ''; +``` + +If these lines are filled, reCaptcha will be used. +Get a key from https://www.google.com/recaptcha/admin/create ### Version 0.8.4: @@ -123,4 +169,3 @@ You can choose between default, bootstrap, gabdark and gabdark3. * Fully standards compliant css and xhtml. * Random generating names for anonymous users * Paste downloading - diff --git a/CREATING_THEMES.md b/doc/CREATING_THEMES.md similarity index 100% rename from CREATING_THEMES.md rename to doc/CREATING_THEMES.md diff --git a/doc/DEVELOPMENT.md b/doc/DEVELOPMENT.md new file mode 100644 index 0000000..91c5333 --- /dev/null +++ b/doc/DEVELOPMENT.md @@ -0,0 +1,9 @@ +You want to improve Stikked? Always welcome! Send us your pull request and you will be honoured in AUTHORS.md. + +Some guidelines: + +* For PHP only files (not views/themes), please run the PHP Beautifier (http://pear.php.net/package/PHP_Beautifier) with the following parameters: +``` +php_beautifier --indent_tabs -l "IndentStyles(style=bsd) ArrayNested() NewLines(before=function:T_CLASS:if,after=T_COMMENT) ListClassFunction()" +``` +* Other people may modify your contribution. Don't take that personal; we all want to improve Stikked. Your input is always welcome! diff --git a/doc/TRANSLATING_STIKKED.md b/doc/TRANSLATING_STIKKED.md new file mode 100644 index 0000000..43474fc --- /dev/null +++ b/doc/TRANSLATING_STIKKED.md @@ -0,0 +1,7 @@ +How to translate Stikked into your own language +----------------------------------------------- + +1. Make a copy of the folder htdocs/application/language/swissgerman, and name it as you like. +2. Start placing in your texts! + +The date_lang.php and form_validation_lang.php are optional; they lay in the system folder for english fallback. stikked_lang.php is required. If you miss a translation, it will be shown as [tanslation_index] in Stikked. diff --git a/doc/TROUBLESHOOTING_GUIDE.md b/doc/TROUBLESHOOTING_GUIDE.md new file mode 100644 index 0000000..37be3ea --- /dev/null +++ b/doc/TROUBLESHOOTING_GUIDE.md @@ -0,0 +1,43 @@ +Stikked troubleshooting guide +----------------------------- + +### Apache + +#### 404 Not Found after creating a Paste + +Rewrite rules must be enabled in httpd.conf. + +### Nginx + +#### 502 Bad Gateway + +PHP FastCGI must be running. Here's my /etc/init.d/php-fcgi config: + +```bash +#!/bin/bash + +FASTCGI_USER=www-data +FASTCGI_GROUP=www-data +ADDRESS=127.0.0.1 +PORT=9000 +PIDFILE=/var/run/php-fastcgi.pid +CHILDREN=6 +PHP5=/usr/bin/php5-cgi + +/usr/bin/spawn-fcgi -a $ADDRESS -p $PORT -P $PIDFILE -C $CHILDREN -u $FASTCGI_USER -g $FASTCGI_GROUP -f $PHP5 +``` + +You can adapt that to your system. + +### Lighttpd + +#### 503 - Service Not Available + +PHP FastCGI must be running. See the php-fcgi section under Nginx. + +### Cherokee + +Still have a problem? +--------------------- + +Report an issue at GitHub, and we will add your problem to this guide. diff --git a/doc/webserver_sampleconfigs/apache-site.conf b/doc/webserver_sampleconfigs/apache-site.conf new file mode 100644 index 0000000..2da6137 --- /dev/null +++ b/doc/webserver_sampleconfigs/apache-site.conf @@ -0,0 +1,12 @@ + + ServerName stikked + DocumentRoot /var/www/stikked/htdocs + ErrorLog ${APACHE_LOG_DIR}/stikked-error_log + CustomLog ${APACHE_LOG_DIR}/stikked-access_log common + + Options +FollowSymLinks + AllowOverride All + Order allow,deny + Allow from all + + diff --git a/doc/webserver_sampleconfigs/cherokee.conf b/doc/webserver_sampleconfigs/cherokee.conf new file mode 100644 index 0000000..3244aa4 --- /dev/null +++ b/doc/webserver_sampleconfigs/cherokee.conf @@ -0,0 +1,325 @@ +config!version = 001002101 +server!bind!1!port = 80 +server!keepalive = 1 +server!keepalive_max_requests = 500 +server!panic_action = /opt/cherokee/bin/cherokee-panic +server!pid_file = /opt/cherokee/var/run/cherokee.pid +server!server_tokens = full +server!timeout = 15 +vserver!1!directory_index = index.html +vserver!1!document_root = /opt/cherokee/var/www +vserver!1!error_writer!filename = /opt/cherokee/var/log/cherokee.error +vserver!1!error_writer!type = file +vserver!1!logger = combined +vserver!1!logger!access!buffsize = 16384 +vserver!1!logger!access!filename = /opt/cherokee/var/log/cherokee.access +vserver!1!logger!access!type = file +vserver!1!nick = default +vserver!1!rule!5!encoder!gzip = allow +vserver!1!rule!5!handler = server_info +vserver!1!rule!5!handler!type = just_about +vserver!1!rule!5!match = directory +vserver!1!rule!5!match!directory = /about +vserver!1!rule!4!document_root = /opt/cherokee/lib/cgi-bin +vserver!1!rule!4!handler = cgi +vserver!1!rule!4!match = directory +vserver!1!rule!4!match!directory = /cgi-bin +vserver!1!rule!3!document_root = /opt/cherokee/share/cherokee/themes +vserver!1!rule!3!handler = file +vserver!1!rule!3!match = directory +vserver!1!rule!3!match!directory = /cherokee_themes +vserver!1!rule!2!document_root = /opt/cherokee/share/cherokee/icons +vserver!1!rule!2!handler = file +vserver!1!rule!2!match = directory +vserver!1!rule!2!match!directory = /icons +vserver!1!rule!1!handler = common +vserver!1!rule!1!handler!iocache = 1 +vserver!1!rule!1!match = default +vserver!2!directory_index = index.php,index.html +vserver!2!document_root = /opt/cherokee/var/www/stikked +vserver!2!error_writer!filename = /opt/cherokee/var/log/cherokee.error +vserver!2!error_writer!type = file +vserver!2!logger = combined +vserver!2!logger!access!buffsize = 16384 +vserver!2!logger!access!filename = /opt/cherokee/var/log/cherokee.access +vserver!2!logger!access!type = file +vserver!2!nick = stikked +vserver!2!rule!110!document_root = /opt/cherokee/var/www/stikked/static +vserver!2!rule!110!handler = file +vserver!2!rule!110!handler!iocache = 0 +vserver!2!rule!110!match = directory +vserver!2!rule!110!match!directory = /static +vserver!2!rule!110!match!final = 1 +vserver!2!rule!10!handler = fcgi +vserver!2!rule!10!handler!balancer = round_robin +vserver!2!rule!10!handler!balancer!source!10 = 1 +vserver!2!rule!10!handler!check_file = 1 +vserver!2!rule!10!handler!error_handler = 1 +vserver!2!rule!10!handler!pass_req_headers = 1 +vserver!2!rule!10!handler!script_alias = /opt/cherokee/var/www/stikked/index.php +vserver!2!rule!10!handler!x_real_ip_enabled = 0 +vserver!2!rule!10!handler!xsendfile = 0 +vserver!2!rule!10!match = default +source!1!env!PHP_FCGI_CHILDREN = 5 +source!1!env!PHP_FCGI_MAX_REQUESTS = 490 +source!1!env_inherited = 0 +source!1!host = 127.0.0.1:47990 +source!1!interpreter = /usr/bin/php-cgi -b 127.0.0.1:47990 +source!1!nick = PHP Interpreter +source!1!type = interpreter +icons!default = page_white.png +icons!directory = folder.png +icons!file!bomb.png = core +icons!file!page_white_go.png = *README* +icons!parent_directory = arrow_turn_left.png +icons!suffix!camera.png = jpg,jpeg,jpe +icons!suffix!cd.png = iso,ngr,cue +icons!suffix!color_wheel.png = png,gif,xcf,bmp,pcx,tiff,tif,cdr,psd,xpm,xbm +icons!suffix!control_play.png = bin,exe,com,msi,out +icons!suffix!css.png = css +icons!suffix!cup.png = java,class,jar +icons!suffix!email.png = eml,mbox,box,email,mbx +icons!suffix!film.png = avi,mpeg,mpe,mpg,mpeg3,dl,fli,qt,mov,movie,flv,webm +icons!suffix!font.png = ttf +icons!suffix!html.png = html,htm +icons!suffix!music.png = au,snd,mid,midi,kar,mpga,mpega,mp2,mp3,sid,wav,aif,aiff,aifc,gsm,m3u,wma,wax,ra,rm,ram,pls,sd2,ogg +icons!suffix!package.png = tar,gz,bz2,zip,rar,ace,lha,7z,dmg,cpk +icons!suffix!page_white_acrobat.png = pdf +icons!suffix!page_white_c.png = c,h,cpp +icons!suffix!page_white_office.png = doc,ppt,xls +icons!suffix!page_white_php.png = php +icons!suffix!page_white_text.png = txt,text,rtf,sdw +icons!suffix!printer.png = ps,eps +icons!suffix!ruby.png = rb +icons!suffix!script.png = sh,csh,ksh,tcl,tk,py,pl +mime!application/bzip2!extensions = bz2 +mime!application/gzip!extensions = gz +mime!application/hta!extensions = hta +mime!application/java-archive!extensions = jar +mime!application/java-serialized-object!extensions = ser +mime!application/java-vm!extensions = class +mime!application/json!extensions = json +mime!application/mac-binhex40!extensions = hqx +mime!application/msaccess!extensions = mdb +mime!application/msword!extensions = doc,dot +mime!application/octet-stream!extensions = bin +mime!application/octetstream!extensions = ace +mime!application/oda!extensions = oda +mime!application/ogg!extensions = ogx +mime!application/pdf!extensions = pdf +mime!application/pgp-keys!extensions = key +mime!application/pgp-signature!extensions = pgp +mime!application/pics-rules!extensions = prf +mime!application/postscript!extensions = ps,ai,eps +mime!application/rar!extensions = rar +mime!application/rdf+xml!extensions = rdf +mime!application/rss+xml!extensions = rss +mime!application/smil!extensions = smi,smil +mime!application/vnd.mozilla.xul+xml!extensions = xul +mime!application/vnd.ms-excel!extensions = xls,xlb,xlt +mime!application/vnd.ms-pki.seccat!extensions = cat +mime!application/vnd.ms-pki.stl!extensions = stl +mime!application/vnd.ms-powerpoint!extensions = ppt,pps +mime!application/vnd.oasis.opendocument.chart!extensions = odc +mime!application/vnd.oasis.opendocument.database!extensions = odb +mime!application/vnd.oasis.opendocument.formula!extensions = odf +mime!application/vnd.oasis.opendocument.graphics!extensions = odg +mime!application/vnd.oasis.opendocument.image!extensions = odi +mime!application/vnd.oasis.opendocument.presentation!extensions = odp +mime!application/vnd.oasis.opendocument.spreadsheet!extensions = ods +mime!application/vnd.oasis.opendocument.text!extensions = odt +mime!application/vnd.oasis.opendocument.text-master!extensions = odm +mime!application/vnd.oasis.opendocument.text-web!extensions = oth +mime!application/vnd.pkg5.info!extensions = p5i +mime!application/vnd.visio!extensions = vsd +mime!application/vnd.wap.wbxml!extensions = wbxml +mime!application/vnd.wap.wmlc!extensions = wmlc +mime!application/vnd.wap.wmlscriptc!extensions = wmlsc +mime!application/x-7z-compressed!extensions = 7z +mime!application/x-abiword!extensions = abw +mime!application/x-apple-diskimage!extensions = dmg +mime!application/x-bcpio!extensions = bcpio +mime!application/x-bittorrent!extensions = torrent +mime!application/x-cdf!extensions = cdf +mime!application/x-cpio!extensions = cpio +mime!application/x-csh!extensions = csh +mime!application/x-debian-package!extensions = deb,udeb +mime!application/x-director!extensions = dcr,dir,dxr +mime!application/x-dvi!extensions = dvi +mime!application/x-flac!extensions = flac +mime!application/x-font!extensions = pfa,pfb,gsf,pcf,pcf.Z +mime!application/x-freemind!extensions = mm +mime!application/x-gnumeric!extensions = gnumeric +mime!application/x-gtar!extensions = gtar,tgz,taz +mime!application/x-gzip!extensions = gz,tgz +mime!application/x-httpd-php!extensions = phtml,pht,php +mime!application/x-httpd-php-source!extensions = phps +mime!application/x-httpd-php3!extensions = php3 +mime!application/x-httpd-php3-preprocessed!extensions = php3p +mime!application/x-httpd-php4!extensions = php4 +mime!application/x-internet-signup!extensions = ins,isp +mime!application/x-iphone!extensions = iii +mime!application/x-iso9660-image!extensions = iso +mime!application/x-java-jnlp-file!extensions = jnlp +mime!application/x-javascript!extensions = js +mime!application/x-kchart!extensions = chrt +mime!application/x-killustrator!extensions = kil +mime!application/x-koan!extensions = skp,skd,skt,skm +mime!application/x-kpresenter!extensions = kpr,kpt +mime!application/x-kspread!extensions = ksp +mime!application/x-kword!extensions = kwd,kwt +mime!application/x-latex!extensions = latex +mime!application/x-lha!extensions = lha +mime!application/x-lzh!extensions = lzh +mime!application/x-lzx!extensions = lzx +mime!application/x-ms-wmd!extensions = wmd +mime!application/x-ms-wmz!extensions = wmz +mime!application/x-msdos-program!extensions = com,exe,bat,dll +mime!application/x-msi!extensions = msi +mime!application/x-netcdf!extensions = nc +mime!application/x-ns-proxy-autoconfig!extensions = pac +mime!application/x-nwc!extensions = nwc +mime!application/x-object!extensions = o +mime!application/x-oz-application!extensions = oza +mime!application/x-pkcs7-certreqresp!extensions = p7r +mime!application/x-pkcs7-crl!extensions = crl +mime!application/x-python-code!extensions = pyc,pyo +mime!application/x-quicktimeplayer!extensions = qtl +mime!application/x-redhat-package-manager!extensions = rpm +mime!application/x-sh!extensions = sh +mime!application/x-shar!extensions = shar +mime!application/x-shockwave-flash!extensions = swf,swfl +mime!application/x-stuffit!extensions = sit,sea +mime!application/x-sv4cpio!extensions = sv4cpio +mime!application/x-sv4crc!extensions = sv4crc +mime!application/x-tar!extensions = tar +mime!application/x-tcl!extensions = tcl +mime!application/x-tex-pk!extensions = pk +mime!application/x-texinfo!extensions = texinfo,texi +mime!application/x-trash!extensions = ~,bak,old,sik +mime!application/x-troff!extensions = t,tr,roff +mime!application/x-troff-man!extensions = man +mime!application/x-troff-me!extensions = me +mime!application/x-troff-ms!extensions = ms +mime!application/x-ustar!extensions = ustar +mime!application/x-x509-ca-cert!extensions = crt +mime!application/x-xcf!extensions = xcf +mime!application/x-xfig!extensions = fig +mime!application/x-xpinstall!extensions = xpi +mime!application/xhtml+xml!extensions = xhtml,xht +mime!application/xml!extensions = xml,xsl +mime!application/zip!extensions = zip +mime!audio/basic!extensions = au,snd +mime!audio/midi!extensions = mid,midi,kar +mime!audio/mpeg!extensions = mpga,mpega,mp2,mp3,m4a +mime!audio/ogg!extensions = ogg,oga +mime!audio/prs.sid!extensions = sid +mime!audio/x-aiff!extensions = aif,aiff,aifc +mime!audio/x-gsm!extensions = gsm +mime!audio/x-mpegurl!extensions = m3u +mime!audio/x-ms-wax!extensions = wax +mime!audio/x-ms-wma!extensions = wma +mime!audio/x-pn-realaudio!extensions = ra,rm,ram +mime!audio/x-realaudio!extensions = ra +mime!audio/x-scpls!extensions = pls +mime!audio/x-sd2!extensions = sd2 +mime!audio/x-wav!extensions = wav +mime!chemical/x-cache!extensions = cac,cache +mime!chemical/x-cache-csf!extensions = csf +mime!chemical/x-cdx!extensions = cdx +mime!chemical/x-cif!extensions = cif +mime!chemical/x-cmdf!extensions = cmdf +mime!chemical/x-cml!extensions = cml +mime!chemical/x-compass!extensions = cpa +mime!chemical/x-crossfire!extensions = bsd +mime!chemical/x-csml!extensions = csml,csm +mime!chemical/x-ctx!extensions = ctx +mime!chemical/x-cxf!extensions = cxf,cef +mime!chemical/x-isostar!extensions = istr,ist +mime!chemical/x-jcamp-dx!extensions = jdx,dx +mime!chemical/x-kinemage!extensions = kin +mime!chemical/x-pdb!extensions = pdb,ent +mime!chemical/x-swissprot!extensions = sw +mime!chemical/x-vamas-iso14976!extensions = vms +mime!chemical/x-vmd!extensions = vmd +mime!chemical/x-xtel!extensions = xtel +mime!chemical/x-xyz!extensions = xyz +mime!image/gif!extensions = gif +mime!image/jpeg!extensions = jpeg,jpg,jpe +mime!image/pcx!extensions = pcx +mime!image/png!extensions = png +mime!image/svg+xml!extensions = svg,svgz +mime!image/tiff!extensions = tiff,tif +mime!image/vnd.djvu!extensions = djvu,djv +mime!image/vnd.wap.wbmp!extensions = wbmp +mime!image/x-icon!extensions = ico +mime!image/x-ms-bmp!extensions = bmp +mime!image/x-photoshop!extensions = psd +mime!image/x-portable-anymap!extensions = pnm +mime!image/x-portable-bitmap!extensions = pbm +mime!image/x-portable-graymap!extensions = pgm +mime!image/x-portable-pixmap!extensions = ppm +mime!image/x-xbitmap!extensions = xbm +mime!image/x-xpixmap!extensions = xpm +mime!image/x-xwindowdump!extensions = xwd +mime!model/iges!extensions = igs,iges +mime!model/mesh!extensions = msh,mesh,silo +mime!model/vrml!extensions = wrl,vrml +mime!text/calendar!extensions = ics,icz +mime!text/comma-separated-values!extensions = csv +mime!text/css!extensions = css +mime!text/h323!extensions = 323 +mime!text/html!extensions = html,htm,shtml +mime!text/iuls!extensions = uls +mime!text/mathml!extensions = mml +mime!text/plain!extensions = asc,txt,text,diff,pot +mime!text/richtext!extensions = rtx +mime!text/rtf!extensions = rtf +mime!text/scriptlet!extensions = sct,wsc +mime!text/tab-separated-values!extensions = tsv +mime!text/vnd.sun.j2me.app-descriptor!extensions = jad +mime!text/vnd.wap.wml!extensions = wml +mime!text/vnd.wap.wmlscript!extensions = wmls +mime!text/x-boo!extensions = boo +mime!text/x-c++hdr!extensions = h++,hpp,hxx,hh +mime!text/x-c++src!extensions = c++,cpp,cxx,cc +mime!text/x-chdr!extensions = h +mime!text/x-csh!extensions = csh +mime!text/x-csrc!extensions = c +mime!text/x-dsrc!extensions = d +mime!text/x-haskell!extensions = hs +mime!text/x-java!extensions = java +mime!text/x-literate-haskell!extensions = lhs +mime!text/x-moc!extensions = moc +mime!text/x-pascal!extensions = p,pas +mime!text/x-pcs-gcd!extensions = gcd +mime!text/x-perl!extensions = pl,pm +mime!text/x-python!extensions = py +mime!text/x-setext!extensions = etx +mime!text/x-sh!extensions = sh +mime!text/x-tcl!extensions = tcl,tk +mime!text/x-tex!extensions = tex,ltx,sty,cls +mime!text/x-vcalendar!extensions = vcs +mime!text/x-vcard!extensions = vcf +mime!video/dl!extensions = dl +mime!video/dv!extensions = dif,dv +mime!video/fli!extensions = fli +mime!video/gl!extensions = gl +mime!video/mp4!extensions = mp4 +mime!video/mpeg!extensions = mpeg,mpg,mpe +mime!video/ogg!extensions = ogv +mime!video/quicktime!extensions = qt,mov +mime!video/vnd.mpegurl!extensions = mxu +mime!video/webm!extensions = webm +mime!video/x-flv!extensions = flv +mime!video/x-la-asf!extensions = lsf,lsx +mime!video/x-mng!extensions = mng +mime!video/x-ms-asf!extensions = asf,asx +mime!video/x-ms-wm!extensions = wm +mime!video/x-ms-wmv!extensions = wmv +mime!video/x-ms-wmx!extensions = wmx +mime!video/x-ms-wvx!extensions = wvx +mime!video/x-msvideo!extensions = avi +mime!video/x-sgi-movie!extensions = movie +mime!x-conference/x-cooltalk!extensions = ice +mime!x-world/x-vrml!extensions = vrm,vrml,wrl \ No newline at end of file diff --git a/doc/webserver_sampleconfigs/lighttpd-site.conf b/doc/webserver_sampleconfigs/lighttpd-site.conf new file mode 100644 index 0000000..677bf78 --- /dev/null +++ b/doc/webserver_sampleconfigs/lighttpd-site.conf @@ -0,0 +1,23 @@ +server.modules += ( "mod_fastcgi" ) +server.modules += ( "mod_rewrite" ) + +fastcgi.server += ( ".php" => + (( + "host" => "127.0.0.1", + "port" => "9000", + "broken-scriptfilename" => "enable" + )) +) + +$HTTP["host"] == "stikked" { + url.rewrite-once = ( + "^/static/(.*)$" => "/static/$1", + "^/favicon\.ico$" => "/favicon.ico", + "^/robots\.txt$" => "/robots.txt", + "^/(.*)$" => "/index.php$2", + ) + + server.document-root = "/var/www/stikked/htdocs" + + #accesslog.filename = "/var/log/lighttpd/stikked.access.log" +} diff --git a/doc/webserver_sampleconfigs/nginx-site.conf b/doc/webserver_sampleconfigs/nginx-site.conf new file mode 100644 index 0000000..b31acfd --- /dev/null +++ b/doc/webserver_sampleconfigs/nginx-site.conf @@ -0,0 +1,23 @@ +server { + listen 80; + server_name stikked; + root /var/www/stikked/htdocs; + index index.php; + +# access_log /var/log/nginx/localhost.access_log combined; +# error_log /var/log/nginx/localhost.error_log info; + + if ($request_uri !~* ^/(static|favicon\.ico|robots\.txt)) + { + rewrite ^/(.*)$ /index.php?/$1 last; + break; + } + + location ~* \.php$ { + fastcgi_pass 127.0.0.1:9000; + fastcgi_index index.php; + fastcgi_split_path_info ^(.+\.php)(.*)$; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + } +} diff --git a/htdocs/application/config/autoload.php b/htdocs/application/config/autoload.php index b602db2..7431c5d 100644 --- a/htdocs/application/config/autoload.php +++ b/htdocs/application/config/autoload.php @@ -64,7 +64,7 @@ $autoload['libraries'] = array('database', 'db_session', 'carabiner'); | $autoload['helper'] = array('url', 'file'); */ -$autoload['helper'] = array('url', 'date'); +$autoload['helper'] = array('url', 'date', 'language'); /* @@ -96,7 +96,7 @@ $autoload['config'] = array('stikked'); | */ -$autoload['language'] = array(); +$autoload['language'] = array('stikked'); /* diff --git a/htdocs/application/config/codemirror_languages.php b/htdocs/application/config/codemirror_languages.php index 40d24c3..ea80c18 100644 --- a/htdocs/application/config/codemirror_languages.php +++ b/htdocs/application/config/codemirror_languages.php @@ -135,10 +135,10 @@ $config['codemirror_languages'] = array( ) , ) , 'sql' => array( - 'mode' => 'mysql', + 'mode' => 'sql', 'js' => array( array( - 'codemirror/mode/mysql/mysql.js' + 'codemirror/mode/sql/sql.js' ) , ) , ) , @@ -157,6 +157,22 @@ $config['codemirror_languages'] = array( '6502tasm' => 'MOS 6502 TASM/64TASS', '68000devpac' => 'Motorola 68000 Devpac Assembler', 'abap' => 'ABAP', + 'apl' => array( + 'mode' => 'apl', + 'js' => array( + array( + 'codemirror/mode/apl/apl.js' + ) , + ) , + ) , + 'asterisk' => array( + 'mode' => 'asterisk', + 'js' => array( + array( + 'codemirror/mode/asterisk/asterisk.js' + ) , + ) , + ) , 'actionscript' => 'Actionscript', 'actionscript3' => 'ActionScript3', 'ada' => 'Ada', @@ -221,7 +237,14 @@ $config['codemirror_languages'] = array( ) , ) , 'cuesheet' => 'Cuesheet', - 'd' => 'D', + 'd' => array( + 'mode' => 'd', + 'js' => array( + array( + 'codemirror/mode/d/d.js' + ) , + ) , + ) , 'dcs' => 'DCS', 'delphi' => 'Delphi (Object Pascal)', 'div' => 'DIV', @@ -239,6 +262,14 @@ $config['codemirror_languages'] = array( 'eiffel' => 'Eiffel', 'email' => 'Email (mbox/eml/RFC format)', 'epc' => 'Enerscript', + 'erlang' => array( + 'mode' => 'erlang', + 'js' => array( + array( + 'codemirror/mode/erlang/erlang.js' + ) , + ) , + ) , 'euphoria' => 'Euphoria', 'f1' => 'Formula One', 'falcon' => 'Falcon', @@ -254,6 +285,14 @@ $config['codemirror_languages'] = array( 'glsl' => 'glSlang', 'gml' => 'GML', 'gnuplot' => 'Gnuplot script', + 'go' => array( + 'mode' => 'go', + 'js' => array( + array( + 'codemirror/mode/go/go.js' + ) , + ) , + ) , 'groovy' => array( 'mode' => 'text/x-groovy', 'js' => array( @@ -271,6 +310,14 @@ $config['codemirror_languages'] = array( ) , ) , ) , + 'haxe' => array( + 'mode' => 'text/x-haxe', + 'js' => array( + array( + 'codemirror/mode/haxe/haxe.js' + ) , + ) , + ) , 'hicest' => 'HicEst', 'hq9plus' => 'HQ9+', 'html4strict' => array( @@ -340,17 +387,24 @@ $config['codemirror_languages'] = array( 'make' => 'Make', 'mapbasic' => 'MapBasic', 'matlab' => 'Matlab M-file', - 'mirc' => 'mIRC Scripting', + 'mirc' => array( + 'mode' => 'mirc', + 'js' => array( + array( + 'codemirror/mode/mirc/mirc.js' + ) , + ) , + ) , 'mmix' => 'MMIX Assembler', 'modula2' => 'Modula-2', 'modula3' => 'Modula-3', 'mpasm' => 'Microchip Assembler', 'mxml' => 'MXML', 'mysql' => array( - 'mode' => 'mysql', + 'mode' => 'sql', 'js' => array( array( - 'codemirror/mode/mysql/mysql.js' + 'codemirror/mode/sql/sql.js' ) , ) , ) , @@ -360,6 +414,14 @@ $config['codemirror_languages'] = array( 'objc' => 'Objective-C', 'objeck' => 'Objeck Programming Language', 'ocaml' => 'OCaml (Objective Caml)', + 'ocaml' => array( + 'mode' => 'text/x-ocaml', + 'js' => array( + array( + 'codemirror/mode/ocaml/ocaml.js' + ) , + ) , + ) , 'oobas' => 'OpenOffice.org Basic', 'oracle11' => 'Oracle 11i', 'oracle8' => 'Oracle 8', @@ -397,10 +459,10 @@ $config['codemirror_languages'] = array( 'pixelbender' => 'Pixel Bender 1.0', 'pli' => 'PL/I', 'plsql' => array( - 'mode' => 'plsql', + 'mode' => 'sql', 'js' => array( array( - 'codemirror/mode/plsql/plsql.js' + 'codemirror/mode/sql/sql.js' ) , ) , ) , @@ -421,7 +483,14 @@ $config['codemirror_languages'] = array( ) , 'providex' => 'ProvideX', 'purebasic' => 'PureBasic', - 'q' => 'q/kdb+', + 'q' => array( + 'mode' => 'text/x-q', + 'js' => array( + array( + 'codemirror/mode/q/q.js' + ) , + ) , + ) , 'qbasic' => 'QBasic/QuickBASIC', 'rails' => 'Ruby (with Ruby on Rails Framework)', 'rebol' => 'Rebol', @@ -430,7 +499,14 @@ $config['codemirror_languages'] = array( 'rpmspec' => 'RPM Spec', 'rsplus' => 'R', 'sas' => 'SAS', - 'scala' => 'Scala', + 'scala' => array( + 'mode' => 'scala', + 'js' => array( + array( + 'codemirror/mode/clike/clike.js' + ) , + ) , + ) , 'scheme' => array( 'mode' => 'text/x-scheme', 'js' => array( @@ -444,7 +520,14 @@ $config['codemirror_languages'] = array( 'smalltalk' => 'Smalltalk', 'smarty' => 'Smarty template', 'systemverilog' => 'SystemVerilog IEEE 1800-2009(draft8)', - 'tcl' => 'TCL/iTCL', + 'tcl' => array( + 'mode' => 'text/x-tcl', + 'js' => array( + array( + 'codemirror/mode/tcl/tcl.js' + ) , + ) , + ) , 'teraterm' => 'Tera Term Macro', 'thinbasic' => 'thinBasic', 'tsql' => 'T-SQL', @@ -453,18 +536,10 @@ $config['codemirror_languages'] = array( 'uscript' => 'UnrealScript', 'vala' => 'Vala', 'vb' => array( - 'mode' => 'text/vbscript', + 'mode' => 'text/x-vb', 'js' => array( array( - 'codemirror/mode/vbscript/vbscript.js' - ) , - ) , - ) , - 'vbnet' => array( - 'mode' => 'text/vbscript', - 'js' => array( - array( - 'codemirror/mode/vbscript/vbscript.js' + 'codemirror/mode/vb/vb.js' ) , ) , ) , @@ -494,6 +569,13 @@ $config['codemirror_languages'] = array( ) , ) , ) , - 'z80' => 'ZiLOG Z80 Assembler', + 'z80' => array( + 'mode' => 'text/x-z80', + 'js' => array( + array( + 'codemirror/mode/z80/z80.js' + ) , + ) , + ) , 'zxbasic' => 'ZXBasic', ); diff --git a/htdocs/application/config/config.php b/htdocs/application/config/config.php index 3ec5aca..3c92c9d 100644 --- a/htdocs/application/config/config.php +++ b/htdocs/application/config/config.php @@ -16,6 +16,16 @@ */ $config['base_url'] = ''; +/* +|-------------------------------------------------------------------------- +| Stikked version +|-------------------------------------------------------------------------- +| +| So that we can track your version. +| +*/ +$config['stikked_version'] = '0.8.5'; + /* |-------------------------------------------------------------------------- | Index File @@ -69,7 +79,7 @@ $config['url_suffix'] = ''; | than english. | */ -$config['language'] = 'english'; +#$config['language'] = 'english'; #this is configured in config/stikked.php /* |-------------------------------------------------------------------------- diff --git a/htdocs/application/config/geshi_languages.php b/htdocs/application/config/geshi_languages.php index c39c58d..f98b42d 100644 --- a/htdocs/application/config/geshi_languages.php +++ b/htdocs/application/config/geshi_languages.php @@ -39,7 +39,9 @@ $config['geshi_languages'] = array( 'apache' => 'Apache', 'applescript' => 'AppleScript', 'apt_sources' => 'Apt sources.list', + 'arm' => 'ARM Assembler', 'asm' => 'x86 Assembler', + 'asymptote' => 'asymptote', 'asp' => 'ASP', 'autoconf' => 'autoconf', 'autohotkey' => 'Autohotkey', @@ -69,6 +71,8 @@ $config['geshi_languages'] = array( 'cuesheet' => 'Cuesheet', 'd' => 'D', 'dcs' => 'DCS', + 'dcl' => 'DCL', + 'dcpu16' => 'DCPU/16 Assembly', 'delphi' => 'Delphi (Object Pascal)', 'div' => 'DIV', 'dos' => 'DOS', @@ -84,6 +88,7 @@ $config['geshi_languages'] = array( 'fo' => 'fo', 'fortran' => 'Fortran', 'freebasic' => 'FreeBasic', + 'freeswitch' => 'FreeSWITCH', 'fsharp' => 'F#', 'gambas' => 'GAMBAS', 'gdb' => 'GDB', @@ -96,6 +101,7 @@ $config['geshi_languages'] = array( 'groovy' => 'Groovy', 'gwbasic' => 'GwBasic', 'haskell' => 'Haskell', + 'haxe' => 'Haxe', 'hicest' => 'HicEst', 'hq9plus' => 'HQ9+', 'html4strict' => 'HTML 4.01 strict', @@ -114,6 +120,7 @@ $config['geshi_languages'] = array( 'klonec' => 'KLone with C', 'klonecpp' => 'KLone with C++', 'lb' => 'Liberty BASIC', + 'ldif' => 'LDIF', 'lisp' => 'Generic Lisp', 'llvm' => 'LLVM', 'locobasic' => 'Locomotive Basic (Amstrad CPC series)', @@ -135,17 +142,23 @@ $config['geshi_languages'] = array( 'mpasm' => 'Microchip Assembler', 'mxml' => 'MXML', 'mysql' => 'MySQL', + 'nagios' => 'Nagios', + 'netrexx' => 'NetRexx', 'newlisp' => 'newLISP', 'nsis' => 'Nullsoft Scriptable Install System', 'oberon2' => 'Oberon-2', 'objc' => 'Objective-C', 'objeck' => 'Objeck Programming Language', 'ocaml' => 'OCaml (Objective Caml)', + 'octave' => 'GNU Octave M-file', 'oobas' => 'OpenOffice.org Basic', + 'oorexx' => 'ooRexx', 'oracle11' => 'Oracle 11i', 'oracle8' => 'Oracle 8', 'oxygene' => 'Delphi Prism (Oxygene)', 'oz' => 'Oz', + 'parasail' => 'ParaSail', + 'parigp' => 'PARI/GP', 'pascal' => 'Pascal', 'pcre' => 'PCRE', 'per' => 'Per (forms)', @@ -167,11 +180,13 @@ $config['geshi_languages'] = array( 'properties' => 'Property', 'providex' => 'ProvideX', 'purebasic' => 'PureBasic', + 'pys60' => 'Python for S60', 'q' => 'q/kdb+', 'qbasic' => 'QBasic/QuickBASIC', 'rails' => 'Ruby (with Ruby on Rails Framework)', 'rebol' => 'Rebol', 'reg' => 'Microsoft Registry Editor', + 'rexx' => 'Rexx', 'robots' => 'robots.txt', 'rpmspec' => 'RPM Spec', 'rsplus' => 'R', @@ -182,6 +197,9 @@ $config['geshi_languages'] = array( 'sdlbasic' => 'sdlBasic', 'smalltalk' => 'Smalltalk', 'smarty' => 'Smarty template', + 'spark' => 'SPARK', + 'sparql' => 'SPARQL', + 'stonescript' => 'StoneScript', 'systemverilog' => 'SystemVerilog IEEE 1800-2009(draft8)', 'tcl' => 'TCL/iTCL', 'teraterm' => 'Tera Term Macro', @@ -190,9 +208,12 @@ $config['geshi_languages'] = array( 'typoscript' => 'TypoScript', 'unicon' => 'Unicon', 'uscript' => 'UnrealScript', + 'upc' => 'UPC', + 'urbi' => 'Urbi', 'vala' => 'Vala', 'vb' => 'Visual Basic', 'vbnet' => 'VB.NET', + 'vedit' => 'Vedit macro language', 'verilog' => 'Verilog', 'vhdl' => 'VHDL', 'vim' => 'Vim scripting', diff --git a/htdocs/application/config/routes.php b/htdocs/application/config/routes.php index eafc508..910b96a 100644 --- a/htdocs/application/config/routes.php +++ b/htdocs/application/config/routes.php @@ -55,6 +55,8 @@ $route['lists/:num'] = 'main/lists/$1'; $route['trends'] = 'main/trends'; $route['trends/:any'] = 'main/trends/$1'; $route['spamadmin/:num'] = 'spamadmin/index'; +$route['spamadmin/blacklist'] = 'spamadmin/blacklist'; +$route['spamadmin/blacklist/(:num)'] = 'spamadmin/blacklist'; $route['spamadmin/blacklist/unblock/(:any)'] = 'spamadmin/unblock_ip'; $route['spamadmin/:any'] = 'spamadmin/spam_detail'; $route['about'] = 'main/about'; @@ -66,6 +68,7 @@ $route['404_override'] = 'main/error_404'; $route['themes/:any/css/:any'] = 'theme_assets/css'; $route['themes/:any/images/:any'] = 'theme_assets/images'; +$route['themes/:any/img/:any'] = 'theme_assets/images'; $route['themes/:any/js/:any'] = 'theme_assets/js'; /* End of file routes.php */ diff --git a/htdocs/application/config/stikked.php b/htdocs/application/config/stikked.php index 082337a..c0a8c0c 100644 --- a/htdocs/application/config/stikked.php +++ b/htdocs/application/config/stikked.php @@ -25,10 +25,21 @@ $config['db_password'] = 'stikked'; * * Which theme to use * Folder name in htdocs/themes/ + * Currently: default, bootstrap, gabdark, gabdark3, geocities * */ $config['theme'] = 'default'; +/** + * Language + * + * Which language to use + * Translate Stikked to your own language, see htdocs/application/language files + * Currently: english, german, swissgerman, spanish + * +*/ +$config['language'] = 'english'; + /** * Combine JS & CSS files (recommended) * @@ -77,11 +88,16 @@ $config['per_page'] = 15; * * private_only: No recent pastes will be displayed. * enable_captcha: Users must enter a captcha to post. + * recaptcha_publickey & recaptcha_privatekey: If filled, reCaptcha will be used (get a key from https://www.google.com/recaptcha/admin/create) * blocked_words: Comma separated list, e.g. '.es.tl, mycraft.com, yourbadword' + * disable_api: Don't allow pasting via API (because we can't use a captcha there...) * **/ $config['private_only'] = false; $config['enable_captcha'] = false; +$config['recaptcha_publickey'] = ''; +$config['recaptcha_privatekey'] = ''; +$config['disable_api'] = false; $config['blocked_words'] = ''; //spamadmin: accessible via /spamadmin (only active when user + pass is set) diff --git a/htdocs/application/controllers/api.php b/htdocs/application/controllers/api.php index 1de7e2b..3b3703c 100644 --- a/htdocs/application/controllers/api.php +++ b/htdocs/application/controllers/api.php @@ -18,6 +18,11 @@ class Api extends Main function __construct() { parent::__construct(); + + if (config_item('disable_api')) + { + die("The API has been disabled\n"); + } } function index() @@ -32,6 +37,8 @@ class Api extends Main function create() { $this->load->model('pastes'); + $this->load->library('form_validation'); //needed by parent class + if (!$this->input->post('text')) { @@ -51,6 +58,20 @@ class Api extends Main { $_POST['private'] = 1; } + + //validations + + if (!$this->_valid_ip()) + { + die("You are not allowed to paste\n"); + } + + if (!$this->_blockwords_check()) + { + die("Your paste contains blocked words\n"); + } + + //create paste $paste_url = $this->pastes->createPaste(); $data['msg'] = base_url() . $paste_url; $this->load->view('view/api', $data); diff --git a/htdocs/application/controllers/main.php b/htdocs/application/controllers/main.php index ae81df7..aa0136d 100644 --- a/htdocs/application/controllers/main.php +++ b/htdocs/application/controllers/main.php @@ -17,6 +17,7 @@ * - captcha() * - _valid_lang() * - _valid_captcha() + * - _valid_recaptcha() * - _valid_ip() * - _blockwords_check() * - _autofill_check() @@ -35,10 +36,20 @@ class Main extends CI_Controller parent::__construct(); $this->load->model('languages'); - if ($this->config->item('require_auth')) + if (config_item('require_auth')) { $this->load->library('auth_ldap'); } + + //recaptcha + $this->recaptcha_publickey = config_item('recaptcha_publickey'); + $this->recaptcha_privatekey = config_item('recaptcha_privatekey'); + + if ($this->recaptcha_publickey != '' && $this->recaptcha_privatekey != '') + { + $this->load->helper('recaptcha'); + $this->use_recaptcha = true; + } if (!$this->db->table_exists('ci_sessions')) { @@ -249,7 +260,7 @@ class Main extends CI_Controller //codemirror languages $this->load->config('codemirror_languages'); - $codemirror_languages = $this->config->item('codemirror_languages'); + $codemirror_languages = config_item('codemirror_languages'); $data['codemirror_languages'] = $codemirror_languages; //codemirror modes @@ -263,13 +274,17 @@ class Main extends CI_Controller } } $data['codemirror_modes'] = $cmm; + + //recaptcha + $data['use_recaptcha'] = $this->use_recaptcha; + $data['recaptcha_publickey'] = $this->recaptcha_publickey; if (!$this->input->post('submit')) { if (!$this->db_session->userdata('expire')) { - $default_expiration = $this->config->item('default_expiration'); + $default_expiration = config_item('default_expiration'); $this->db_session->set_userdata('expire', $default_expiration); } @@ -287,7 +302,7 @@ class Main extends CI_Controller if (!$lang) { - $lang = $this->config->item('default_language'); + $lang = config_item('default_language'); } $data['lang_set'] = $lang; } @@ -356,7 +371,7 @@ class Main extends CI_Controller //form validation $this->form_validation->set_rules($rules); - $this->form_validation->set_message('min_length', 'The %s field can not be empty'); + $this->form_validation->set_message('min_length', lang('empty')); $this->form_validation->set_error_delimiters('
', '
'); if ($this->form_validation->run() == FALSE) @@ -367,7 +382,7 @@ class Main extends CI_Controller else { - if ($this->config->item('private_only')) + if (config_item('private_only')) { $_POST['private'] = 1; } @@ -416,7 +431,7 @@ class Main extends CI_Controller $this->load->helper('text'); $paste = $this->pastes->getPaste(3); $data = $this->pastes->getReplies(3); - $data['page_title'] = $paste['title'] . ' - ' . $this->config->item('site_name'); + $data['page_title'] = $paste['title'] . ' - ' . config_item('site_name'); $data['feed_url'] = site_url('view/rss/' . $this->uri->segment(3)); $this->load->view('view/rss', $data); } @@ -434,7 +449,7 @@ class Main extends CI_Controller if ($check) { - $data = $this->pastes->getPaste(3); + $data = $this->pastes->getPaste(3, true, $this->uri->segment(4) == 'diff'); $this->load->view('view/embed', $data); } else @@ -464,7 +479,7 @@ class Main extends CI_Controller { $this->_valid_authentication(); - if ($this->config->item('private_only')) + if (config_item('private_only')) { show_404(); } @@ -476,7 +491,7 @@ class Main extends CI_Controller if ($this->uri->segment(2) == 'rss') { $this->load->helper('text'); - $data['page_title'] = $this->config->item('site_name'); + $data['page_title'] = config_item('site_name'); $data['feed_url'] = site_url('lists/rss'); $data['replies'] = $data['pastes']; unset($data['pastes']); @@ -493,7 +508,7 @@ class Main extends CI_Controller { $this->_valid_authentication(); - if ($this->config->item('private_only')) + if (config_item('private_only')) { show_404(); } @@ -519,7 +534,7 @@ class Main extends CI_Controller { redirect('view/raw/' . $this->uri->segment(2)); } - $data = $this->pastes->getPaste(2, true); + $data = $this->pastes->getPaste(2, true, $this->uri->segment(3) == 'diff'); $data['reply_form'] = $this->_form_prep($data['lang_code'], 'Re: ' . $data['title'], $data['raw'], $data['pid']); $this->load->view('view/view', $data); } @@ -534,7 +549,7 @@ class Main extends CI_Controller $this->load->model('pastes'); $key = $this->uri->segment(2); - if ($key != $this->config->item('cron_key')) + if ($key != config_item('cron_key')) { show_404(); } @@ -577,17 +592,25 @@ class Main extends CI_Controller function _valid_lang($lang) { $this->load->model('languages'); - $this->form_validation->set_message('_valid_lang', 'Please select your language'); + $this->form_validation->set_message('_valid_lang', lang('valid_lang')); return $this->languages->valid_language($lang); } function _valid_captcha($text) { - if ($this->config->item('enable_captcha')) + if (config_item('enable_captcha')) { - $this->form_validation->set_message('_valid_captcha', 'The Captcha is incorrect.'); - return strtolower($text) == strtolower($this->db_session->userdata('captcha')); + $this->form_validation->set_message('_valid_captcha', lang('captcha')); + + if ($this->use_recaptcha) + { + return $this->_valid_recaptcha(); + } + else + { + return strtolower($text) == strtolower($this->db_session->userdata('captcha')); + } } else { @@ -595,6 +618,26 @@ class Main extends CI_Controller } } + function _valid_recaptcha() + { + + if ($this->input->post('recaptcha_response_field')) + { + $pk = $this->recaptcha_privatekey; + $ra = $_SERVER['REMOTE_ADDR']; + $cf = $this->input->post('recaptcha_challenge_field'); + $rf = $this->input->post('recaptcha_response_field'); + + //check + $resp = recaptcha_check_answer($pk, $ra, $cf, $rf); + return $resp->is_valid; + } + else + { + return false; + } + } + function _valid_ip() { @@ -604,7 +647,7 @@ class Main extends CI_Controller $ip_firstpart = $ip[0] . '.' . $ip[1] . '.'; //setup message - $this->form_validation->set_message('_valid_ip', 'You are not allowed to paste.'); + $this->form_validation->set_message('_valid_ip', lang('not_allowed')); //lookup $this->db->select('ip_address, spam_attempts'); @@ -637,12 +680,19 @@ class Main extends CI_Controller { //setup message - $this->form_validation->set_message('_blockwords_check', 'Your paste contains blocked words.'); + $this->form_validation->set_message('_blockwords_check', lang('blocked_words')); //check - $blocked_words = $this->config->item('blocked_words'); + $blocked_words = config_item('blocked_words'); $post = $this->input->post(); $raw = $post['code']; + + if (!$blocked_words) + { + return true; + } + + //we have blocked words foreach (explode(',', $blocked_words) as $word) { $word = trim($word); @@ -659,16 +709,16 @@ class Main extends CI_Controller { //setup message - $this->form_validation->set_message('_autofill_check', 'Go away, robot!'); + $this->form_validation->set_message('_autofill_check', lang('robot')); //check - return !$this->input->post('email'); + return (!$this->input->post('email') && !$this->input->post('url')); } function _valid_authentication() { - if ($this->config->item('require_auth')) + if (config_item('require_auth')) { if (!$this->auth_ldap->is_authenticated()) @@ -683,14 +733,23 @@ class Main extends CI_Controller { $lang = $this->uri->segment(3); $this->load->config('codemirror_languages'); - $cml = $this->config->item('codemirror_languages'); + $cml = config_item('codemirror_languages'); + + //file path + $file_path = 'themes/' . config_item('theme') . '/js/'; + + if (!file_exists($file_path)) + { + $file_path = 'themes/default/js/'; + } if (isset($cml[$lang]) && gettype($cml[$lang]) == 'array') { header('Content-Type: application/x-javascript; charset=utf-8'); + header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', time() + 60 * 60 * 24 * 30)); foreach ($cml[$lang]['js'] as $js) { - echo file_get_contents('./static/js/' . $js[0]); + echo file_get_contents($file_path . $js[0]); } } exit; diff --git a/htdocs/application/controllers/theme_assets.php b/htdocs/application/controllers/theme_assets.php index 5d02a88..b1a6b47 100644 --- a/htdocs/application/controllers/theme_assets.php +++ b/htdocs/application/controllers/theme_assets.php @@ -55,7 +55,8 @@ class Theme_assets extends CI_Controller } //send - header('Content-type: ' . mime_content_type($file_path)); + $size = getimagesize($file_path); + header('Content-type: ' . $size['mime']); $this->_expires_header(30); readfile($file_path); } @@ -74,8 +75,15 @@ class Theme_assets extends CI_Controller //file path $file_path = 'themes/' . $this->theme . '/js/' . $js_file; + //fallback to default js if js in theme not found + + if (!file_exists($file_path)) + { + $file_path = 'themes/default/js/' . $js_file; + } + //send - header('Content-type: application/x-javascript'); + header('Content-Type: application/x-javascript; charset=utf-8'); $this->_expires_header(30); readfile($file_path); } diff --git a/htdocs/application/errors/error_404.php b/htdocs/application/errors/error_404.php index 0b2aef9..8beb033 100644 --- a/htdocs/application/errors/error_404.php +++ b/htdocs/application/errors/error_404.php @@ -9,9 +9,9 @@ header("HTTP/1.1 404 Not Found"); Stikked - - - + + + @@ -19,14 +19,6 @@ header("HTTP/1.1 404 Not Found");

config->item('site_name'); ?>

-
- -
@@ -41,10 +33,6 @@ header("HTTP/1.1 404 Not Found");
- diff --git a/htdocs/application/helpers/language_helper.php b/htdocs/application/helpers/language_helper.php new file mode 100644 index 0000000..567a41a --- /dev/null +++ b/htdocs/application/helpers/language_helper.php @@ -0,0 +1,58 @@ +lang->line($index); + + if ($id != '') + { + $line = '"; + } + + return ($line != '' ? $line : '[' . $index . ']'); + } +} + +// ------------------------------------------------------------------------ +/* End of file language_helper.php */ +/* Location: ./system/helpers/language_helper.php */ diff --git a/htdocs/application/helpers/recaptcha_helper.php b/htdocs/application/helpers/recaptcha_helper.php new file mode 100644 index 0000000..32c4f4d --- /dev/null +++ b/htdocs/application/helpers/recaptcha_helper.php @@ -0,0 +1,277 @@ + $value ) + $req .= $key . '=' . urlencode( stripslashes($value) ) . '&'; + + // Cut the last '&' + $req=substr($req,0,strlen($req)-1); + return $req; +} + + + +/** + * Submits an HTTP POST to a reCAPTCHA server + * @param string $host + * @param string $path + * @param array $data + * @param int port + * @return array response + */ +function _recaptcha_http_post($host, $path, $data, $port = 80) { + + $req = _recaptcha_qsencode ($data); + + $http_request = "POST $path HTTP/1.0\r\n"; + $http_request .= "Host: $host\r\n"; + $http_request .= "Content-Type: application/x-www-form-urlencoded;\r\n"; + $http_request .= "Content-Length: " . strlen($req) . "\r\n"; + $http_request .= "User-Agent: reCAPTCHA/PHP\r\n"; + $http_request .= "\r\n"; + $http_request .= $req; + + $response = ''; + if( false == ( $fs = @fsockopen($host, $port, $errno, $errstr, 10) ) ) { + die ('Could not open socket'); + } + + fwrite($fs, $http_request); + + while ( !feof($fs) ) + $response .= fgets($fs, 1160); // One TCP-IP packet + fclose($fs); + $response = explode("\r\n\r\n", $response, 2); + + return $response; +} + + + +/** + * Gets the challenge HTML (javascript and non-javascript version). + * This is called from the browser, and the resulting reCAPTCHA HTML widget + * is embedded within the HTML form it was called from. + * @param string $pubkey A public key for reCAPTCHA + * @param string $error The error given by reCAPTCHA (optional, default is null) + * @param boolean $use_ssl Should the request be made over ssl? (optional, default is false) + + * @return string - The HTML to be embedded in the user's form. + */ +function recaptcha_get_html ($pubkey, $error = null, $use_ssl = false) +{ + if ($pubkey == null || $pubkey == '') { + die ("To use reCAPTCHA you must get an API key from https://www.google.com/recaptcha/admin/create"); + } + + if ($use_ssl) { + $server = RECAPTCHA_API_SECURE_SERVER; + } else { + $server = RECAPTCHA_API_SERVER; + } + + $errorpart = ""; + if ($error) { + $errorpart = "&error=" . $error; + } + return ' + + '; +} + + + + +/** + * A ReCaptchaResponse is returned from recaptcha_check_answer() + */ +class ReCaptchaResponse { + var $is_valid; + var $error; +} + + +/** + * Calls an HTTP POST function to verify if the user's guess was correct + * @param string $privkey + * @param string $remoteip + * @param string $challenge + * @param string $response + * @param array $extra_params an array of extra variables to post to the server + * @return ReCaptchaResponse + */ +function recaptcha_check_answer ($privkey, $remoteip, $challenge, $response, $extra_params = array()) +{ + if ($privkey == null || $privkey == '') { + die ("To use reCAPTCHA you must get an API key from https://www.google.com/recaptcha/admin/create"); + } + + if ($remoteip == null || $remoteip == '') { + die ("For security reasons, you must pass the remote ip to reCAPTCHA"); + } + + + + //discard spam submissions + if ($challenge == null || strlen($challenge) == 0 || $response == null || strlen($response) == 0) { + $recaptcha_response = new ReCaptchaResponse(); + $recaptcha_response->is_valid = false; + $recaptcha_response->error = 'incorrect-captcha-sol'; + return $recaptcha_response; + } + + $response = _recaptcha_http_post (RECAPTCHA_VERIFY_SERVER, "/recaptcha/api/verify", + array ( + 'privatekey' => $privkey, + 'remoteip' => $remoteip, + 'challenge' => $challenge, + 'response' => $response + ) + $extra_params + ); + + $answers = explode ("\n", $response [1]); + $recaptcha_response = new ReCaptchaResponse(); + + if (trim ($answers [0]) == 'true') { + $recaptcha_response->is_valid = true; + } + else { + $recaptcha_response->is_valid = false; + $recaptcha_response->error = $answers [1]; + } + return $recaptcha_response; + +} + +/** + * gets a URL where the user can sign up for reCAPTCHA. If your application + * has a configuration page where you enter a key, you should provide a link + * using this function. + * @param string $domain The domain where the page is hosted + * @param string $appname The name of your application + */ +function recaptcha_get_signup_url ($domain = null, $appname = null) { + return "https://www.google.com/recaptcha/admin/create?" . _recaptcha_qsencode (array ('domains' => $domain, 'app' => $appname)); +} + +function _recaptcha_aes_pad($val) { + $block_size = 16; + $numpad = $block_size - (strlen ($val) % $block_size); + return str_pad($val, strlen ($val) + $numpad, chr($numpad)); +} + +/* Mailhide related code */ + +function _recaptcha_aes_encrypt($val,$ky) { + if (! function_exists ("mcrypt_encrypt")) { + die ("To use reCAPTCHA Mailhide, you need to have the mcrypt php module installed."); + } + $mode=MCRYPT_MODE_CBC; + $enc=MCRYPT_RIJNDAEL_128; + $val=_recaptcha_aes_pad($val); + return mcrypt_encrypt($enc, $ky, $val, $mode, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); +} + + +function _recaptcha_mailhide_urlbase64 ($x) { + return strtr(base64_encode ($x), '+/', '-_'); +} + +/* gets the reCAPTCHA Mailhide url for a given email, public key and private key */ +function recaptcha_mailhide_url($pubkey, $privkey, $email) { + if ($pubkey == '' || $pubkey == null || $privkey == "" || $privkey == null) { + die ("To use reCAPTCHA Mailhide, you have to sign up for a public and private key, " . + "you can do so at http://www.google.com/recaptcha/mailhide/apikey"); + } + + + $ky = pack('H*', $privkey); + $cryptmail = _recaptcha_aes_encrypt ($email, $ky); + + return "http://www.google.com/recaptcha/mailhide/d?k=" . $pubkey . "&c=" . _recaptcha_mailhide_urlbase64 ($cryptmail); +} + +/** + * gets the parts of the email to expose to the user. + * eg, given johndoe@example,com return ["john", "example.com"]. + * the email is then displayed as john...@example.com + */ +function _recaptcha_mailhide_email_parts ($email) { + $arr = preg_split("/@/", $email ); + + if (strlen ($arr[0]) <= 4) { + $arr[0] = substr ($arr[0], 0, 1); + } else if (strlen ($arr[0]) <= 6) { + $arr[0] = substr ($arr[0], 0, 3); + } else { + $arr[0] = substr ($arr[0], 0, 4); + } + return $arr; +} + +/** + * Gets html to display an email address given a public an private key. + * to get a key, go to: + * + * http://www.google.com/recaptcha/mailhide/apikey + */ +function recaptcha_mailhide_html($pubkey, $privkey, $email) { + $emailparts = _recaptcha_mailhide_email_parts ($email); + $url = recaptcha_mailhide_url ($pubkey, $privkey, $email); + + return htmlentities($emailparts[0]) . "...@" . htmlentities ($emailparts [1]); + +} + + +?> diff --git a/htdocs/application/language/english/date_lang.php b/htdocs/application/language/english/date_lang.php new file mode 100644 index 0000000..c0ace16 --- /dev/null +++ b/htdocs/application/language/english/date_lang.php @@ -0,0 +1,61 @@ + - - 403 Forbidden - - - -

Directory access is forbidden.

- - - \ No newline at end of file diff --git a/htdocs/application/language/english/stikked_lang.php b/htdocs/application/language/english/stikked_lang.php new file mode 100644 index 0000000..0eef887 --- /dev/null +++ b/htdocs/application/language/english/stikked_lang.php @@ -0,0 +1,71 @@ +userdata['last_activity'] = $this->now; // format query array to update database $ud = $this->userdata; + + //fix for https://github.com/claudehohl/Stikked/issues/52 + if(!isset($ud['user_agent']) + || !isset($ud['ip_address']) + || !isset($ud['session_id'])) + { + return false; + } + $query_array = array( 'last_activity' => $ud['last_activity'], 'user_agent' => $ud['user_agent'], 'ip_address' => $ud['ip_address'] ); diff --git a/htdocs/application/libraries/finediff.php b/htdocs/application/libraries/finediff.php new file mode 100644 index 0000000..2e38b77 --- /dev/null +++ b/htdocs/application/libraries/finediff.php @@ -0,0 +1,688 @@ +copy->insert +* command (swap) for when the inserted segment is exactly the same +* as the deleted one, and with only a copy operation in between. +* TODO: How often this case occurs? Is it worth it? Can only +* be done as a postprocessing method (->optimize()?) +*/ +abstract class FineDiffOp { + abstract public function getFromLen(); + abstract public function getToLen(); + abstract public function getOpcode(); + } + +class FineDiffDeleteOp extends FineDiffOp { + public function __construct($len) { + $this->fromLen = $len; + } + public function getFromLen() { + return $this->fromLen; + } + public function getToLen() { + return 0; + } + public function getOpcode() { + if ( $this->fromLen === 1 ) { + return 'd'; + } + return "d{$this->fromLen}"; + } + } + +class FineDiffInsertOp extends FineDiffOp { + public function __construct($text) { + $this->text = $text; + } + public function getFromLen() { + return 0; + } + public function getToLen() { + return strlen($this->text); + } + public function getText() { + return $this->text; + } + public function getOpcode() { + $to_len = strlen($this->text); + if ( $to_len === 1 ) { + return "i:{$this->text}"; + } + return "i{$to_len}:{$this->text}"; + } + } + +class FineDiffReplaceOp extends FineDiffOp { + public function __construct($fromLen, $text) { + $this->fromLen = $fromLen; + $this->text = $text; + } + public function getFromLen() { + return $this->fromLen; + } + public function getToLen() { + return strlen($this->text); + } + public function getText() { + return $this->text; + } + public function getOpcode() { + if ( $this->fromLen === 1 ) { + $del_opcode = 'd'; + } + else { + $del_opcode = "d{$this->fromLen}"; + } + $to_len = strlen($this->text); + if ( $to_len === 1 ) { + return "{$del_opcode}i:{$this->text}"; + } + return "{$del_opcode}i{$to_len}:{$this->text}"; + } + } + +class FineDiffCopyOp extends FineDiffOp { + public function __construct($len) { + $this->len = $len; + } + public function getFromLen() { + return $this->len; + } + public function getToLen() { + return $this->len; + } + public function getOpcode() { + if ( $this->len === 1 ) { + return 'c'; + } + return "c{$this->len}"; + } + public function increase($size) { + return $this->len += $size; + } + } + +/** +* FineDiff ops +* +* Collection of ops +*/ +class FineDiffOps { + public function appendOpcode($opcode, $from, $from_offset, $from_len) { + if ( $opcode === 'c' ) { + $edits[] = new FineDiffCopyOp($from_len); + } + else if ( $opcode === 'd' ) { + $edits[] = new FineDiffDeleteOp($from_len); + } + else /* if ( $opcode === 'i' ) */ { + $edits[] = new FineDiffInsertOp(substr($from, $from_offset, $from_len)); + } + } + public $edits = array(); + } + +/** +* FineDiff class +* +* TODO: Document +* +*/ +class FineDiff { + + /**------------------------------------------------------------------------ + * + * Public section + * + */ + + /** + * Constructor + * ... + * The $granularityStack allows FineDiff to be configurable so that + * a particular stack tailored to the specific content of a document can + * be passed. + */ + public function __construct($from_text = '', $to_text = '', $granularityStack = null) { + // setup stack for generic text documents by default + $this->granularityStack = $granularityStack ? $granularityStack : FineDiff::$characterGranularity; + $this->edits = array(); + $this->from_text = $from_text; + $this->doDiff($from_text, $to_text); + } + + public function getOps() { + return $this->edits; + } + + public function getOpcodes() { + $opcodes = array(); + foreach ( $this->edits as $edit ) { + $opcodes[] = $edit->getOpcode(); + } + return implode('', $opcodes); + } + + public function renderDiffToHTML() { + $in_offset = 0; + ob_start(); + foreach ( $this->edits as $edit ) { + $n = $edit->getFromLen(); + if ( $edit instanceof FineDiffCopyOp ) { + FineDiff::renderDiffToHTMLFromOpcode('c', $this->from_text, $in_offset, $n); + } + else if ( $edit instanceof FineDiffDeleteOp ) { + FineDiff::renderDiffToHTMLFromOpcode('d', $this->from_text, $in_offset, $n); + } + else if ( $edit instanceof FineDiffInsertOp ) { + FineDiff::renderDiffToHTMLFromOpcode('i', $edit->getText(), 0, $edit->getToLen()); + } + else /* if ( $edit instanceof FineDiffReplaceOp ) */ { + FineDiff::renderDiffToHTMLFromOpcode('d', $this->from_text, $in_offset, $n); + FineDiff::renderDiffToHTMLFromOpcode('i', $edit->getText(), 0, $edit->getToLen()); + } + $in_offset += $n; + } + return ob_get_clean(); + } + + /**------------------------------------------------------------------------ + * Return an opcodes string describing the diff between a "From" and a + * "To" string + */ + public static function getDiffOpcodes($from, $to, $granularities = null) { + $diff = new FineDiff($from, $to, $granularities); + return $diff->getOpcodes(); + } + + /**------------------------------------------------------------------------ + * Return an iterable collection of diff ops from an opcodes string + */ + public static function getDiffOpsFromOpcodes($opcodes) { + $diffops = new FineDiffOps(); + FineDiff::renderFromOpcodes(null, $opcodes, array($diffops,'appendOpcode')); + return $diffops->edits; + } + + /**------------------------------------------------------------------------ + * Re-create the "To" string from the "From" string and an "Opcodes" string + */ + public static function renderToTextFromOpcodes($from, $opcodes) { + ob_start(); + FineDiff::renderFromOpcodes($from, $opcodes, array('FineDiff','renderToTextFromOpcode')); + return ob_get_clean(); + } + + /**------------------------------------------------------------------------ + * Render the diff to an HTML string + */ + public static function renderDiffToHTMLFromOpcodes($from, $opcodes) { + ob_start(); + FineDiff::renderFromOpcodes($from, $opcodes, array('FineDiff','renderDiffToHTMLFromOpcode')); + return ob_get_clean(); + } + + /**------------------------------------------------------------------------ + * Generic opcodes parser, user must supply callback for handling + * single opcode + */ + public static function renderFromOpcodes($from, $opcodes, $callback) { + if ( !is_callable($callback) ) { + return; + } + $opcodes_len = strlen($opcodes); + $from_offset = $opcodes_offset = 0; + while ( $opcodes_offset < $opcodes_len ) { + $opcode = substr($opcodes, $opcodes_offset, 1); + $opcodes_offset++; + $n = intval(substr($opcodes, $opcodes_offset)); + if ( $n ) { + $opcodes_offset += strlen(strval($n)); + } + else { + $n = 1; + } + if ( $opcode === 'c' ) { // copy n characters from source + call_user_func($callback, 'c', $from, $from_offset, $n, ''); + $from_offset += $n; + } + else if ( $opcode === 'd' ) { // delete n characters from source + call_user_func($callback, 'd', $from, $from_offset, $n, ''); + $from_offset += $n; + } + else /* if ( $opcode === 'i' ) */ { // insert n characters from opcodes + call_user_func($callback, 'i', $opcodes, $opcodes_offset + 1, $n); + $opcodes_offset += 1 + $n; + } + } + } + + /** + * Stock granularity stacks and delimiters + */ + + const paragraphDelimiters = "\n\r"; + public static $paragraphGranularity = array( + FineDiff::paragraphDelimiters + ); + const sentenceDelimiters = ".\n\r"; + public static $sentenceGranularity = array( + FineDiff::paragraphDelimiters, + FineDiff::sentenceDelimiters + ); + const wordDelimiters = " \t.\n\r"; + public static $wordGranularity = array( + FineDiff::paragraphDelimiters, + FineDiff::sentenceDelimiters, + FineDiff::wordDelimiters + ); + const characterDelimiters = ""; + public static $characterGranularity = array( + FineDiff::paragraphDelimiters, + FineDiff::sentenceDelimiters, + FineDiff::wordDelimiters, + FineDiff::characterDelimiters + ); + + public static $textStack = array( + ".", + " \t.\n\r", + "" + ); + + /**------------------------------------------------------------------------ + * + * Private section + * + */ + + /** + * Entry point to compute the diff. + */ + private function doDiff($from_text, $to_text) { + $this->last_edit = false; + $this->stackpointer = 0; + $this->from_text = $from_text; + $this->from_offset = 0; + // can't diff without at least one granularity specifier + if ( empty($this->granularityStack) ) { + return; + } + $this->_processGranularity($from_text, $to_text); + } + + /** + * This is the recursive function which is responsible for + * handling/increasing granularity. + * + * Incrementally increasing the granularity is key to compute the + * overall diff in a very efficient way. + */ + private function _processGranularity($from_segment, $to_segment) { + $delimiters = $this->granularityStack[$this->stackpointer++]; + $has_next_stage = $this->stackpointer < count($this->granularityStack); + foreach ( FineDiff::doFragmentDiff($from_segment, $to_segment, $delimiters) as $fragment_edit ) { + // increase granularity + if ( $fragment_edit instanceof FineDiffReplaceOp && $has_next_stage ) { + $this->_processGranularity( + substr($this->from_text, $this->from_offset, $fragment_edit->getFromLen()), + $fragment_edit->getText() + ); + } + // fuse copy ops whenever possible + else if ( $fragment_edit instanceof FineDiffCopyOp && $this->last_edit instanceof FineDiffCopyOp ) { + $this->edits[count($this->edits)-1]->increase($fragment_edit->getFromLen()); + $this->from_offset += $fragment_edit->getFromLen(); + } + else { + /* $fragment_edit instanceof FineDiffCopyOp */ + /* $fragment_edit instanceof FineDiffDeleteOp */ + /* $fragment_edit instanceof FineDiffInsertOp */ + $this->edits[] = $this->last_edit = $fragment_edit; + $this->from_offset += $fragment_edit->getFromLen(); + } + } + $this->stackpointer--; + } + + /** + * This is the core algorithm which actually perform the diff itself, + * fragmenting the strings as per specified delimiters. + * + * This function is naturally recursive, however for performance purpose + * a local job queue is used instead of outright recursivity. + */ + private static function doFragmentDiff($from_text, $to_text, $delimiters) { + // Empty delimiter means character-level diffing. + // In such case, use code path optimized for character-level + // diffing. + if ( empty($delimiters) ) { + return FineDiff::doCharDiff($from_text, $to_text); + } + + $result = array(); + + // fragment-level diffing + $from_text_len = strlen($from_text); + $to_text_len = strlen($to_text); + $from_fragments = FineDiff::extractFragments($from_text, $delimiters); + $to_fragments = FineDiff::extractFragments($to_text, $delimiters); + + $jobs = array(array(0, $from_text_len, 0, $to_text_len)); + + $cached_array_keys = array(); + + while ( $job = array_pop($jobs) ) { + + // get the segments which must be diff'ed + list($from_segment_start, $from_segment_end, $to_segment_start, $to_segment_end) = $job; + + // catch easy cases first + $from_segment_length = $from_segment_end - $from_segment_start; + $to_segment_length = $to_segment_end - $to_segment_start; + if ( !$from_segment_length || !$to_segment_length ) { + if ( $from_segment_length ) { + $result[$from_segment_start * 4] = new FineDiffDeleteOp($from_segment_length); + } + else if ( $to_segment_length ) { + $result[$from_segment_start * 4 + 1] = new FineDiffInsertOp(substr($to_text, $to_segment_start, $to_segment_length)); + } + continue; + } + + // find longest copy operation for the current segments + $best_copy_length = 0; + + $from_base_fragment_index = $from_segment_start; + + $cached_array_keys_for_current_segment = array(); + + while ( $from_base_fragment_index < $from_segment_end ) { + $from_base_fragment = $from_fragments[$from_base_fragment_index]; + $from_base_fragment_length = strlen($from_base_fragment); + // performance boost: cache array keys + if ( !isset($cached_array_keys_for_current_segment[$from_base_fragment]) ) { + if ( !isset($cached_array_keys[$from_base_fragment]) ) { + $to_all_fragment_indices = $cached_array_keys[$from_base_fragment] = array_keys($to_fragments, $from_base_fragment, true); + } + else { + $to_all_fragment_indices = $cached_array_keys[$from_base_fragment]; + } + // get only indices which falls within current segment + if ( $to_segment_start > 0 || $to_segment_end < $to_text_len ) { + $to_fragment_indices = array(); + foreach ( $to_all_fragment_indices as $to_fragment_index ) { + if ( $to_fragment_index < $to_segment_start ) { continue; } + if ( $to_fragment_index >= $to_segment_end ) { break; } + $to_fragment_indices[] = $to_fragment_index; + } + $cached_array_keys_for_current_segment[$from_base_fragment] = $to_fragment_indices; + } + else { + $to_fragment_indices = $to_all_fragment_indices; + } + } + else { + $to_fragment_indices = $cached_array_keys_for_current_segment[$from_base_fragment]; + } + // iterate through collected indices + foreach ( $to_fragment_indices as $to_base_fragment_index ) { + $fragment_index_offset = $from_base_fragment_length; + // iterate until no more match + for (;;) { + $fragment_from_index = $from_base_fragment_index + $fragment_index_offset; + if ( $fragment_from_index >= $from_segment_end ) { + break; + } + $fragment_to_index = $to_base_fragment_index + $fragment_index_offset; + if ( $fragment_to_index >= $to_segment_end ) { + break; + } + if ( $from_fragments[$fragment_from_index] !== $to_fragments[$fragment_to_index] ) { + break; + } + $fragment_length = strlen($from_fragments[$fragment_from_index]); + $fragment_index_offset += $fragment_length; + } + if ( $fragment_index_offset > $best_copy_length ) { + $best_copy_length = $fragment_index_offset; + $best_from_start = $from_base_fragment_index; + $best_to_start = $to_base_fragment_index; + } + } + $from_base_fragment_index += strlen($from_base_fragment); + // If match is larger than half segment size, no point trying to find better + // TODO: Really? + if ( $best_copy_length >= $from_segment_length / 2) { + break; + } + // no point to keep looking if what is left is less than + // current best match + if ( $from_base_fragment_index + $best_copy_length >= $from_segment_end ) { + break; + } + } + + if ( $best_copy_length ) { + $jobs[] = array($from_segment_start, $best_from_start, $to_segment_start, $best_to_start); + $result[$best_from_start * 4 + 2] = new FineDiffCopyOp($best_copy_length); + $jobs[] = array($best_from_start + $best_copy_length, $from_segment_end, $best_to_start + $best_copy_length, $to_segment_end); + } + else { + $result[$from_segment_start * 4 ] = new FineDiffReplaceOp($from_segment_length, substr($to_text, $to_segment_start, $to_segment_length)); + } + } + + ksort($result, SORT_NUMERIC); + return array_values($result); + } + + /** + * Perform a character-level diff. + * + * The algorithm is quite similar to doFragmentDiff(), except that + * the code path is optimized for character-level diff -- strpos() is + * used to find out the longest common subequence of characters. + * + * We try to find a match using the longest possible subsequence, which + * is at most the length of the shortest of the two strings, then incrementally + * reduce the size until a match is found. + * + * I still need to study more the performance of this function. It + * appears that for long strings, the generic doFragmentDiff() is more + * performant. For word-sized strings, doCharDiff() is somewhat more + * performant. + */ + private static function doCharDiff($from_text, $to_text) { + $result = array(); + $jobs = array(array(0, strlen($from_text), 0, strlen($to_text))); + while ( $job = array_pop($jobs) ) { + // get the segments which must be diff'ed + list($from_segment_start, $from_segment_end, $to_segment_start, $to_segment_end) = $job; + $from_segment_len = $from_segment_end - $from_segment_start; + $to_segment_len = $to_segment_end - $to_segment_start; + + // catch easy cases first + if ( !$from_segment_len || !$to_segment_len ) { + if ( $from_segment_len ) { + $result[$from_segment_start * 4 + 0] = new FineDiffDeleteOp($from_segment_len); + } + else if ( $to_segment_len ) { + $result[$from_segment_start * 4 + 1] = new FineDiffInsertOp(substr($to_text, $to_segment_start, $to_segment_len)); + } + continue; + } + if ( $from_segment_len >= $to_segment_len ) { + $copy_len = $to_segment_len; + while ( $copy_len ) { + $to_copy_start = $to_segment_start; + $to_copy_start_max = $to_segment_end - $copy_len; + while ( $to_copy_start <= $to_copy_start_max ) { + $from_copy_start = strpos(substr($from_text, $from_segment_start, $from_segment_len), substr($to_text, $to_copy_start, $copy_len)); + if ( $from_copy_start !== false ) { + $from_copy_start += $from_segment_start; + break 2; + } + $to_copy_start++; + } + $copy_len--; + } + } + else { + $copy_len = $from_segment_len; + while ( $copy_len ) { + $from_copy_start = $from_segment_start; + $from_copy_start_max = $from_segment_end - $copy_len; + while ( $from_copy_start <= $from_copy_start_max ) { + $to_copy_start = strpos(substr($to_text, $to_segment_start, $to_segment_len), substr($from_text, $from_copy_start, $copy_len)); + if ( $to_copy_start !== false ) { + $to_copy_start += $to_segment_start; + break 2; + } + $from_copy_start++; + } + $copy_len--; + } + } + // match found + if ( $copy_len ) { + $jobs[] = array($from_segment_start, $from_copy_start, $to_segment_start, $to_copy_start); + $result[$from_copy_start * 4 + 2] = new FineDiffCopyOp($copy_len); + $jobs[] = array($from_copy_start + $copy_len, $from_segment_end, $to_copy_start + $copy_len, $to_segment_end); + } + // no match, so delete all, insert all + else { + $result[$from_segment_start * 4] = new FineDiffReplaceOp($from_segment_len, substr($to_text, $to_segment_start, $to_segment_len)); + } + } + ksort($result, SORT_NUMERIC); + return array_values($result); + } + + /** + * Efficiently fragment the text into an array according to + * specified delimiters. + * No delimiters means fragment into single character. + * The array indices are the offset of the fragments into + * the input string. + * A sentinel empty fragment is always added at the end. + * Careful: No check is performed as to the validity of the + * delimiters. + */ + private static function extractFragments($text, $delimiters) { + // special case: split into characters + if ( empty($delimiters) ) { + $chars = str_split($text, 1); + $chars[strlen($text)] = ''; + return $chars; + } + $fragments = array(); + $start = $end = 0; + for (;;) { + $end += strcspn($text, $delimiters, $end); + $end += strspn($text, $delimiters, $end); + if ( $end === $start ) { + break; + } + $fragments[$start] = substr($text, $start, $end - $start); + $start = $end; + } + $fragments[$start] = ''; + return $fragments; + } + + /** + * Stock opcode renderers + */ + private static function renderToTextFromOpcode($opcode, $from, $from_offset, $from_len) { + if ( $opcode === 'c' || $opcode === 'i' ) { + echo substr($from, $from_offset, $from_len); + } + } + + private static function renderDiffToHTMLFromOpcode($opcode, $from, $from_offset, $from_len) { + if ( $opcode === 'c' ) { + echo htmlentities(htmlentities(substr($from, $from_offset, $from_len))); + } + else if ( $opcode === 'd' ) { + $deletion = substr($from, $from_offset, $from_len); + if ( strcspn($deletion, " \n\r") === 0 ) { + $deletion = str_replace(array("\n","\r"), array('\n','\r'), $deletion); + } + echo '', htmlentities(htmlentities($deletion)), ''; + } + else /* if ( $opcode === 'i' ) */ { + echo '', htmlentities(htmlentities(substr($from, $from_offset, $from_len))), ''; + } + } + } + diff --git a/htdocs/application/libraries/geshi/contrib/aliased.php b/htdocs/application/libraries/geshi/contrib/aliased.php index e57b495..cee3128 100644 --- a/htdocs/application/libraries/geshi/contrib/aliased.php +++ b/htdocs/application/libraries/geshi/contrib/aliased.php @@ -13,7 +13,7 @@ * aliased.php/file.name.ext. * * @author Ross Golder - * @version $Id: aliased.php 881 2007-01-10 11:14:38Z oracleshinoda $ + * @version $Id: aliased.php 2533 2012-08-15 18:49:04Z benbe $ */ // Your config here @@ -29,19 +29,18 @@ $path = SOURCE_ROOT.$_SERVER['PATH_INFO']; $base_path_len = strlen(SOURCE_ROOT); $real_path = realpath($path); if(strncmp($real_path, SOURCE_ROOT, $base_path_len)) { - exit("Stop that."); + exit("Access outside acceptable path."); } // Check file exists if(!file_exists($path)) { - exit("File not found ($path)."); + exit("File not found ($path)."); } -// Gather contents -$contents = file_get_contents($path); - // Prepare GeSHi instance -$geshi =& new GeSHi($contents, "PHP"); +$geshi = new GeSHi(); +$geshi->set_language('text'); +$geshi->load_from_file($path); $geshi->set_header_type(GESHI_HEADER_PRE); $geshi->enable_classes(); $geshi->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS, 10); @@ -50,7 +49,7 @@ $geshi->set_line_style('font: normal normal 95% \'Courier New\', Courier, monosp $geshi->set_code_style('color: #000020;', 'color: #000020;'); $geshi->set_link_styles(GESHI_LINK, 'color: #000060;'); $geshi->set_link_styles(GESHI_HOVER, 'background-color: #f0f000;'); -$geshi->set_header_content('Source code viewer'); +$geshi->set_header_content('Source code viewer - ' . $path . ' - ' . $geshi->get_language_name()); $geshi->set_header_content_style('font-family: Verdana, Arial, sans-serif; color: #808080; font-size: 70%; font-weight: bold; background-color: #f0f0ff; border-bottom: 1px solid #d0d0d0; padding: 2px;'); $geshi->set_footer_content('Parsed in